<?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>DEV Community: Monica Granbois</title>
    <description>The latest articles on DEV Community by Monica Granbois (@monicag).</description>
    <link>https://dev.to/monicag</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%2F19981%2F0782bacb-7ca5-4103-a48f-3caf7e191f25.png</url>
      <title>DEV Community: Monica Granbois</title>
      <link>https://dev.to/monicag</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/monicag"/>
    <language>en</language>
    <item>
      <title>Using React to visualize the knapsack algorithm</title>
      <dc:creator>Monica Granbois</dc:creator>
      <pubDate>Tue, 14 Jun 2022 16:35:27 +0000</pubDate>
      <link>https://dev.to/monicag/using-react-to-visualize-the-knapsack-algorithm-o7h</link>
      <guid>https://dev.to/monicag/using-react-to-visualize-the-knapsack-algorithm-o7h</guid>
      <description>&lt;p&gt;React and Tailwind CSS are popular front-end technologies. I had not used either and wanted to try them. After completing some React tutorials, I wanted to create my own app. I decided to create an &lt;a href="https://monicagranbois.com/knapsack-algorithm-visualization/"&gt;app&lt;/a&gt; that would step through the knapsack algorithm. This would help me solidify my understanding of the algorithm while learning React and Tailwind CSS. This post lists the design and technology choices I made to build the app. For details about the algorithm itself, please visit &lt;a href="https://monicagranbois.com/knapsack-algorithm-visualization/"&gt;https://monicagranbois.com/knapsack-algorithm-visualization/&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Approach
&lt;/h2&gt;

&lt;p&gt;My approach was to build a skeleton of the app first, without any CSS. This allowed me to focus on React and the logic of the app. &lt;/p&gt;

&lt;p&gt;Once I had the basic logic working I  added Tailwind CSS to the project. This allowed me to then focus on learning Tailwind CSS.&lt;/p&gt;

&lt;p&gt;I then iterated on the project using React and Tailwind CSS at the same time.&lt;/p&gt;

&lt;p&gt;I think this was a good approach since I was learning two technologies. It allowed me to focus on one technology at a time. Now that I am familiar with both I would approach a new project differently.  In the future, I would iterate on a project with React and Tailwind CSS at the same time.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Technologies Used
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://create-react-app.dev/"&gt;Create React App&lt;/a&gt; 

&lt;ul&gt;
&lt;li&gt;I chose Create React App (CRA) because I was creating a new, standalone React App.&lt;/li&gt;
&lt;li&gt;This was my first time using CRA, as the tutorials only used React in an index.html file.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://tailwindcss.com/"&gt;Tailwind CSS&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;To get started I followed the tutorials on the &lt;a href="https://www.youtube.com/tailwindlabs"&gt;Tailwinds YouTube channel&lt;/a&gt;. The documentation and online playground were also helpful.&lt;/li&gt;
&lt;li&gt;I used the &lt;a href="https://heroicons.com/"&gt;Hero Icons&lt;/a&gt; project to find and add icons to my project.&lt;/li&gt;
&lt;li&gt;Overall, I liked Tailwind CSS, but it felt verbose to apply styles in the HTML rather than in a separate CSS file.

&lt;ul&gt;
&lt;li&gt;The developer addressed the issue of separation of concerns in his blog post &lt;a href="https://adamwathan.me/css-utility-classes-and-separation-of-concerns/"&gt;CSS Utility Classes and "Separation of Concerns"&lt;/a&gt;. &lt;/li&gt;
&lt;li&gt;I did define custom classes for styles used in multiple places. &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://react-hook-form.com/"&gt;React Hook Form&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;I initially wrote my own form validation. That was the wrong approach.&lt;/li&gt;
&lt;li&gt;I found React Hook Form, which made form validation much easier. &lt;/li&gt;
&lt;li&gt;It also allowed me to use dynamically-generated fields. This allowed me to add, edit and delete items on the initial setup screen.&lt;/li&gt;
&lt;li&gt;I would recommend investigating this tool if your app uses forms.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/react-syntax-highlighter/react-syntax-highlighter"&gt;react-syntax-highlighter&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;I used this to highlight code segments when the app steps through the code.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/msaracevic/react-embed-gist"&gt;react-embed-gist&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;I used this to display the complete algorithm at the end. I used this rather than react-syntax-highlighter because gist has better copy support. If someone wants to copy the algorithm, it is easier for them to do so with the gist.&lt;/li&gt;
&lt;li&gt;The downside is it performs a network call to get the gist.&lt;/li&gt;
&lt;li&gt;I am not sure if this was the right approach.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pages.github.com/"&gt;GitHub Pages&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;I use this to host the app. My blog is already hosted on GitHub Pages, so it seemed a natural place to host it.&lt;/li&gt;
&lt;li&gt;I use a GitHub workflow to deploy the site.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please try the app at &lt;a href="https://monicagranbois.com/knapsack-algorithm-visualization/"&gt;https://monicagranbois.com/knapsack-algorithm-visualization/&lt;/a&gt;. I would appreciate any feedback on it or my tech choices. Happy coding!&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>react</category>
    </item>
    <item>
      <title>Install PostgreSQL onto Ubuntu multipass vm</title>
      <dc:creator>Monica Granbois</dc:creator>
      <pubDate>Tue, 15 Feb 2022 22:18:10 +0000</pubDate>
      <link>https://dev.to/monicag/install-postgresql-onto-ubuntu-multipass-vm-511p</link>
      <guid>https://dev.to/monicag/install-postgresql-onto-ubuntu-multipass-vm-511p</guid>
      <description>&lt;p&gt;I recently installed PostgreSQL on a virtual machine on my dev computer. This post describes what I did to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;install a vm&lt;/li&gt;
&lt;li&gt;install PostgreSQL&lt;/li&gt;
&lt;li&gt;access PostgreSQL from the host machine via &lt;a href="https://www.pgadmin.org/" rel="noopener noreferrer"&gt;pgAdmin&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;install a sample database into PostgreSQL&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I decided to use &lt;a href="https://multipass.run/" rel="noopener noreferrer"&gt;Ubuntu multipass&lt;/a&gt; to create the vm. I had not used it before, and this was a chance to experiment with it. Multipass lets you spin up vm instances from the command line. I found the &lt;a href="https://multipass.run/docs" rel="noopener noreferrer"&gt;installation docs&lt;/a&gt; easy to follow and had an Ubuntu vm running in a few minutes. &lt;/p&gt;

&lt;h2&gt;
  
  
  Install Multipass
&lt;/h2&gt;

&lt;p&gt;I have a Mac, so I had a couple of options to install Multipass: &lt;a href="https://brew.sh/" rel="noopener noreferrer"&gt;brew&lt;/a&gt; or a direct install. I chose to install via brew:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;brew &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--cask&lt;/span&gt; multipass
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some notes on my install:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I used the default hyperkit driver for the hypervisor. 

&lt;ul&gt;
&lt;li&gt;Multipass defaults to hyperkit; but, you can set &lt;a href="https://www.virtualbox.org/" rel="noopener noreferrer"&gt;VirtualBox&lt;/a&gt; as the hypervisor. &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;I set the terminal application to iTerm following &lt;a href="https://multipass.run/docs/changing-macos-terminal" rel="noopener noreferrer"&gt;these instructions&lt;/a&gt;
&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;After the installation process, I checked that multipass was installed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;multipass version
multipass   1.8.1+mac
multipassd  1.8.1+mac
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create a VM
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;launch&lt;/code&gt; command is used to create a new vm. You can provide it a name or multipass can generate one for you. I chose to provide a name, &lt;em&gt;db-server&lt;/em&gt;, using the &lt;code&gt;--name&lt;/code&gt; parameter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;multipass launch &lt;span class="nt"&gt;--name&lt;/span&gt; db-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are two ways two access the vm:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;by opening a shell in the instance&lt;/li&gt;
&lt;li&gt;by using the &lt;code&gt;exec&lt;/code&gt; command to execute commands directly. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I chose to open a shell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;multipass shell db-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The shell will display some stats, including the ipaddress of the vm. &lt;strong&gt;Make note of the ipaddress, as it will be needed&lt;/strong&gt; when connecting to the database at a later step. &lt;/p&gt;

&lt;h2&gt;
  
  
  Install PostgreSQL
&lt;/h2&gt;

&lt;p&gt;I installed the latest version using &lt;a href="https://www.postgresql.org/download/linux/ubuntu/" rel="noopener noreferrer"&gt;the instructions on the PostgreSQL Ubuntu page&lt;/a&gt;. The following commands are copied from that page. Please note, the following are all performed within the &lt;code&gt;db-server&lt;/code&gt; shell.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create the file repository configuration:&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" &amp;gt; /etc/apt/sources.list.d/pgdg.list'&lt;/span&gt;

&lt;span class="c"&gt;# Import the repository signing key:&lt;/span&gt;
wget &lt;span class="nt"&gt;--quiet&lt;/span&gt; &lt;span class="nt"&gt;-O&lt;/span&gt; - https://www.postgresql.org/media/keys/ACCC4CF8.asc | &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-key add -

&lt;span class="c"&gt;# Update the package lists:&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update

&lt;span class="c"&gt;# Install the latest version of PostgreSQL.&lt;/span&gt;
&lt;span class="c"&gt;# If you want a specific version, use 'postgresql-12' or similar instead of 'postgresql':&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nb"&gt;install &lt;/span&gt;postgresql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Installing PostgreSQL creates a &lt;code&gt;postgres&lt;/code&gt; account on the Ubuntu vm. I logged into it to access psql:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ubuntu@db-server:&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;su - postgres
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;postgres@db-server:~&lt;span class="nv"&gt;$ &lt;/span&gt;psql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following was displayed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;psql &lt;span class="o"&gt;(&lt;/span&gt;14.2 &lt;span class="o"&gt;(&lt;/span&gt;Ubuntu 14.2-1.pgdg20.04+1&lt;span class="o"&gt;))&lt;/span&gt;
Type &lt;span class="s2"&gt;"help"&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;help.

&lt;span class="nv"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c"&gt;#&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The postgres user does not have a password. It can be set using the &lt;code&gt;\password&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;psql &lt;span class="o"&gt;(&lt;/span&gt;14.2 &lt;span class="o"&gt;(&lt;/span&gt;Ubuntu 14.2-1.pgdg20.04+1&lt;span class="o"&gt;))&lt;/span&gt;
Type &lt;span class="s2"&gt;"help"&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;help.

&lt;span class="nv"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c"&gt;# \password&lt;/span&gt;
Enter new password &lt;span class="k"&gt;for &lt;/span&gt;user &lt;span class="s2"&gt;"postgres"&lt;/span&gt;:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To exit postgres use the &lt;code&gt;\q&lt;/code&gt; command&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup DB Access
&lt;/h2&gt;

&lt;p&gt;PostgreSQL is installed on the vm, but it is not yet accessible from the Host machine (my macOS). The PostgreSQL config files will need to be modified. The files are located in &lt;code&gt;/etc/postgresql/{version}/main/&lt;/code&gt; directory. In my case the files are in &lt;code&gt;/etc/postgresql/14/main/&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; The following is not a secure way to setup PostgreSQL. See the &lt;a href="https://www.postgresql.org/docs/14/admin.html" rel="noopener noreferrer"&gt;PostgreSQL documentation&lt;/a&gt; for information on setting up PostgreSQL securely.&lt;/p&gt;

&lt;p&gt;Note: I was logged in as the &lt;code&gt;postgres&lt;/code&gt; user while making modifications.&lt;/p&gt;

&lt;h3&gt;
  
  
  postgresql.conf
&lt;/h3&gt;

&lt;p&gt;This file contains settings such as default storage location and memory allocation. It also configures the IPAddresses that PostgreSQL will listen on.&lt;/p&gt;

&lt;p&gt;Find this line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#listen_addresses = 'localhost'         # what IP address(es) to listen on;&lt;/span&gt;
                                        &lt;span class="c"&gt;# comma-separated list of addresses;&lt;/span&gt;
                                        &lt;span class="c"&gt;# defaults to 'localhost'; use '*' for all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remove the &lt;code&gt;#&lt;/code&gt; to uncomment the line and change localhost to "*"&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;listen_addresses &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'*'&lt;/span&gt;              &lt;span class="c"&gt;# what IP address(es) to listen on;&lt;/span&gt;
                                        &lt;span class="c"&gt;# comma-separated list of addresses;&lt;/span&gt;
                                        &lt;span class="c"&gt;# defaults to 'localhost'; use '*' for all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  pg_hba.conf
&lt;/h3&gt;

&lt;p&gt;This file manages security. It controls user authentication, database access and which ipaddresses are allowed to connect. Entries are in the form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;CONNECTION_TYPE     DATABASE    USER    ADDRESS     METHOD
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following line to the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;host    all             all             samenet                 md5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above line will allow TCP/IP connections for all databases and users where the host is on the same subnet as the server. The connection method is "md5".&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;host - This is the connection type. "host" means a TCP/IP socket (either encrypted or not).&lt;/li&gt;
&lt;li&gt;all - The first &lt;code&gt;all&lt;/code&gt; is the database that is allowed to be connected to. In this case all databases can be connected to.&lt;/li&gt;
&lt;li&gt;all - The second &lt;code&gt;all&lt;/code&gt; is the user that can connect. In this case all users can connect.&lt;/li&gt;
&lt;li&gt;samenet - This is the host address that is allowed to connect. Using &lt;code&gt;samenet&lt;/code&gt; means that any address in the subnet the server is on is allowed to connect. &lt;/li&gt;
&lt;li&gt;md5 - use &lt;a href="https://www.postgresql.org/docs/14/auth-password.html" rel="noopener noreferrer"&gt;md5 authentication&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;PostgreSQL will need to be restarted in order for the changes to take effect. Exit the &lt;code&gt;postgres&lt;/code&gt; user back to the &lt;code&gt;ubuntu&lt;/code&gt; user and restart PostgreSQL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart postgresql.service
&lt;span class="c"&gt;# check that PostgreSQL is ready after the restart&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;pg_isready
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  pgAdmin
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.pgadmin.org/" rel="noopener noreferrer"&gt;pgAdmin&lt;/a&gt; is installed on my host machine (MacOS). I want to use it to connect to the PostgreSQL installed on the Ubuntu vm. I used the 'Add Server' wizard to connect to the database with the following information&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;General -&amp;gt; Name: db-server (although this could be anything you want)&lt;/li&gt;
&lt;li&gt;Connection -&amp;gt; Host name/Address: 192.168.64.3 (The IPAddress of my Ubuntu vm)&lt;/li&gt;
&lt;li&gt;Connection -&amp;gt; Port: 5432 (the default PostgreSQL port)&lt;/li&gt;
&lt;li&gt;Connection -&amp;gt; Maintenance database: postgres&lt;/li&gt;
&lt;li&gt;Connection -&amp;gt; Username: postgres&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Install a sample database
&lt;/h2&gt;

&lt;p&gt;Next, I installed a sample database because I wanted some tables and data to play with. Googling turned up many options. I chose the &lt;a href="https://github.com/pthom/northwind_psql" rel="noopener noreferrer"&gt;northwind sample database for psql&lt;/a&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  Create Role
&lt;/h3&gt;

&lt;p&gt;First, I created a role to use with the northwind db. I created a new role by right clicking on the "db-server" menu option and selecting "Create -&amp;gt; Login/Group Roles...". The &lt;a href="https://www.pgadmin.org/docs/pgadmin4/latest/role_dialog.html" rel="noopener noreferrer"&gt;pgAdmin documentation&lt;/a&gt; has details on all the various fields. In short, I did the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;General -&amp;gt; Name: north&lt;/li&gt;
&lt;li&gt;Definition -&amp;gt; Password: &lt;em&gt;super secret password&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Privileges -&amp;gt; Can login: Yes&lt;/li&gt;
&lt;li&gt;Privileges -&amp;gt; Superuser: No&lt;/li&gt;
&lt;li&gt;Privileges -&amp;gt; Create roles: No&lt;/li&gt;
&lt;li&gt;Privileges -&amp;gt; Create databases: Yes&lt;/li&gt;
&lt;li&gt;Privileges -&amp;gt; Inherit rights from the parent roles: Yes&lt;/li&gt;
&lt;li&gt;Privileges -&amp;gt; Can initiate streaming replication and backups: No&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The sql displayed in the SQL tab was the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;ROLE&lt;/span&gt; &lt;span class="n"&gt;north&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt;
    &lt;span class="n"&gt;LOGIN&lt;/span&gt;
    &lt;span class="n"&gt;NOSUPERUSER&lt;/span&gt;
    &lt;span class="k"&gt;CREATEDB&lt;/span&gt;
    &lt;span class="n"&gt;NOCREATEROLE&lt;/span&gt;
    &lt;span class="n"&gt;INHERIT&lt;/span&gt;
    &lt;span class="n"&gt;NOREPLICATION&lt;/span&gt;
    &lt;span class="k"&gt;CONNECTION&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;PASSWORD&lt;/span&gt; &lt;span class="s1"&gt;'xxxxxx'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;COMMENT&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;ROLE&lt;/span&gt; &lt;span class="n"&gt;north&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="s1"&gt;'The user for the northwind database'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After clicking the save button the 'north' role was created.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create the database
&lt;/h3&gt;

&lt;p&gt;I created a database by right clicking on the "db-server" menu option and selecting "Create -&amp;gt; Database...". The &lt;a href="https://www.pgadmin.org/docs/pgadmin4/latest/database_dialog.html" rel="noopener noreferrer"&gt;pgAdmin documentation&lt;/a&gt; details how to create a database. I did the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;General -&amp;gt; Database: northwind&lt;/li&gt;
&lt;li&gt;General -&amp;gt; Owner: north&lt;/li&gt;
&lt;li&gt;Definition -&amp;gt; Encoding: UTF8 (This is the encoding used by the sample database)&lt;/li&gt;
&lt;li&gt;Definition -&amp;gt; Template: template1&lt;/li&gt;
&lt;li&gt;Definition -&amp;gt; Tablespace: pg_default&lt;/li&gt;
&lt;li&gt;Definition -&amp;gt; Collation: C.UTF-8&lt;/li&gt;
&lt;li&gt;Definition -&amp;gt; Character Type: C.UTF-8&lt;/li&gt;
&lt;li&gt;Definition -&amp;gt; Connection limit: -1&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The sql displayed in the SQL tab was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;northwind&lt;/span&gt;
    &lt;span class="k"&gt;WITH&lt;/span&gt; 
    &lt;span class="k"&gt;OWNER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;north&lt;/span&gt;
    &lt;span class="k"&gt;TEMPLATE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;template1&lt;/span&gt;
    &lt;span class="k"&gt;ENCODING&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'UTF8'&lt;/span&gt;
    &lt;span class="n"&gt;LC_COLLATE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'C.UTF-8'&lt;/span&gt;
    &lt;span class="n"&gt;LC_CTYPE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'C.UTF-8'&lt;/span&gt;
    &lt;span class="n"&gt;TABLESPACE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pg_default&lt;/span&gt;
    &lt;span class="k"&gt;CONNECTION&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;COMMENT&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;northwind&lt;/span&gt;
    &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="s1"&gt;'Sample database'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Load the database
&lt;/h3&gt;

&lt;p&gt;I opened the &lt;a href="https://www.pgadmin.org/docs/pgadmin4/latest/query_tool.html" rel="noopener noreferrer"&gt;Query Tool&lt;/a&gt; by drilling down to db-server -&amp;gt; Databases -&amp;gt; northwind, right clicking and choosing Query Tool. I am logged in as the postgres user, so any SQL I run will be under that account. I want to use the north account instead. The user can be changed by clicking on the connection drop down in the Query Tool editor.&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%2Fmonicagranbois.com%2Fimages%2FpgAdminQueryTool.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%2Fmonicagranbois.com%2Fimages%2FpgAdminQueryTool.png" alt="The connection menu dropdown"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After connecting as the &lt;code&gt;north&lt;/code&gt; user the connection information should be: &lt;code&gt;northwind/north@db-server&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next, I download the northwind sample database from GitHub to my host machine (MacOS):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl https://raw.githubusercontent.com/pthom/northwind_psql/master/northwind.sql &lt;span class="nt"&gt;-o&lt;/span&gt; northwind.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I then loaded the tables using the Query Tool in pgAdmin. The steps I used were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open File

&lt;ul&gt;
&lt;li&gt;Choose northwind.sql file that was just downloaded&lt;/li&gt;
&lt;li&gt;This will display the contents of the file into the editor&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Click 'Execute/Run'&lt;/li&gt;

&lt;li&gt;Right click on &lt;code&gt;db-server&lt;/code&gt; and choose refresh.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Now, drilling down through the menus: Databases -&amp;gt; northwind -&amp;gt; Schemas -&amp;gt; public -&amp;gt; Tables displays 14 tables.&lt;/p&gt;

&lt;p&gt;The tables can also be viewed on the guest machine (Ubuntu). As the postgres user, log into the northwind database and use the &lt;code&gt;\dt&lt;/code&gt; command to list the tables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;postgres@db-server:~&lt;span class="nv"&gt;$ &lt;/span&gt;psql northwind
psql &lt;span class="o"&gt;(&lt;/span&gt;14.2 &lt;span class="o"&gt;(&lt;/span&gt;Ubuntu 14.2-1.pgdg20.04+1&lt;span class="o"&gt;))&lt;/span&gt;
Type &lt;span class="s2"&gt;"help"&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;help.

&lt;span class="nv"&gt;northwind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c"&gt;# \dt         &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Stop/Start the VM
&lt;/h2&gt;

&lt;p&gt;To shutdown the Ubuntu vm use the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;multipass stop db-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To restart the server use the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;multipass start db-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I now have a vm running PostgreSQL! &lt;/p&gt;

</description>
      <category>postgres</category>
      <category>database</category>
      <category>multipass</category>
      <category>vm</category>
    </item>
    <item>
      <title>How to mock nanoid</title>
      <dc:creator>Monica Granbois</dc:creator>
      <pubDate>Wed, 17 Nov 2021 00:43:10 +0000</pubDate>
      <link>https://dev.to/monicag/how-to-mock-nanoid-3g0i</link>
      <guid>https://dev.to/monicag/how-to-mock-nanoid-3g0i</guid>
      <description>&lt;p&gt;This short blog post describes how to use &lt;a href="https://jestjs.io/"&gt;Jest&lt;/a&gt; to mock &lt;a href="https://github.com/ai/nanoid"&gt;nanoid&lt;/a&gt;. The nanoid function generates a unique string id. I used it to generate an id for an object. However, I wanted a stable id when unit testing the code. To accomplish this, I mocked the nanoid module and function by doing the following:&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;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nanoid&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;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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;nanoid&lt;/span&gt;&lt;span class="p"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1234&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;The above code does the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;jest.mock("nanoid"...)&lt;/code&gt; - This part mocks the nanoid module.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;return { nanoid: () =&amp;gt; "1234" };&lt;/code&gt; - This part mocks the nanoid function. "1234" is returned when the nanoid function is called.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Below, is a simple example of a React app using the nanoid function and a unit test mocking it.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;code&gt;App.js&lt;/code&gt;&lt;/strong&gt;
&lt;/h4&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;nanoid&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="s2"&gt;nanoid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Item&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;constructor&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="nx"&gt;price&lt;/span&gt;&lt;span class="p"&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;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nanoid&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;//use nanoid to generate a unique id&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;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;name&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;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;price&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="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Display&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;item&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;h2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h2&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="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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;/p&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="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&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="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&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;/p&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="kd"&gt;function&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;apple&lt;/span&gt;&lt;span class="dl"&gt;"&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="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="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="s2"&gt;App&lt;/span&gt;&lt;span class="dl"&gt;"&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;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Nanoid&lt;/span&gt; &lt;span class="nx"&gt;Unit&lt;/span&gt; &lt;span class="nx"&gt;Test&lt;/span&gt; &lt;span class="nx"&gt;Example&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Display&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Display&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Item&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;code&gt;App.test.js&lt;/code&gt;&lt;/strong&gt;
&lt;/h4&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;screen&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="s2"&gt;@testing-library/react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&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;Display&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Item&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="s2"&gt;./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;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nanoid&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;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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;nanoid&lt;/span&gt;&lt;span class="p"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1234&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;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test the Display&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;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should display the item info&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;span class="o"&gt;=&amp;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;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Orange&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&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;Display&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/id: 1234/i&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBeInTheDocument&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/name: Orange/i&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBeInTheDocument&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/price: 5/i&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBeInTheDocument&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;Note: As of this writing CodeSandbox &lt;a href="https://github.com/codesandbox/codesandbox-client/issues/513"&gt;does not support Jest mocks&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  References:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://jestjs.io/docs/mock-functions#mocking-partials"&gt;Jest Mock Functions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>testing</category>
      <category>react</category>
      <category>jest</category>
    </item>
    <item>
      <title>How to free up disk space for XCode Beta install</title>
      <dc:creator>Monica Granbois</dc:creator>
      <pubDate>Thu, 09 Sep 2021 18:02:14 +0000</pubDate>
      <link>https://dev.to/monicag/how-to-free-up-disk-space-for-xcode-beta-install-1i82</link>
      <guid>https://dev.to/monicag/how-to-free-up-disk-space-for-xcode-beta-install-1i82</guid>
      <description>&lt;p&gt;TL;DR - delete Time Machine local snapshots to free up space.&lt;/p&gt;

&lt;p&gt;Does anyone else find installing XCode frustrating? I always need more free disk space, especially when installing a beta. This was the case when I downloaded XCode 13 Beta 5 from the Apple Developer Downloads page. When I tried to extract the xip file I got an error stating:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The archive XCode_13_beta_5.xip can't be expanded because the current volume doesn't have enough free space."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I deleted XCode simulators and cache files to free up space with the following steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to Apple icon -&amp;gt; About This Mac &lt;/li&gt;
&lt;li&gt;Click on 'Storage' tab&lt;/li&gt;
&lt;li&gt;Click 'Manage' Button&lt;/li&gt;
&lt;li&gt;In the Developer menu delete:

&lt;ul&gt;
&lt;li&gt;the XCode Cache file&lt;/li&gt;
&lt;li&gt;old iOS and watchOS Device support files&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;I also delete unused applications, log files, and music to clear up space. After much pruning, I had over 100GB free. Still, the Archive Utility app complained there was not enough free space!&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1435299567583600645-611" src="https://platform.twitter.com/embed/Tweet.html?id=1435299567583600645"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1435299567583600645-611');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1435299567583600645&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;This seemed odd. I tried the solution proposed in this blog post: &lt;a href="https://medium.com/geekculture/installing-xcode-with-not-enough-disk-space-available-b96c8f17115b" rel="noopener noreferrer"&gt;Installing Xcode with “not enough disk space available”&lt;/a&gt;. It didn't work for me. But I suggest trying it out.&lt;/p&gt;

&lt;p&gt;However, the blog post did lead me to check my disk space using the Disk Utility app (Applications -&amp;gt; Utilities -&amp;gt; Disk Utility). It reported I only had ~25GB free when the "About This Mac" storage screen reported I had ~100GB.&lt;/p&gt;

&lt;p&gt;After some googling I found this question on StackExchange: &lt;a href="https://apple.stackexchange.com/q/324440" rel="noopener noreferrer"&gt;macOS not showing the actual free space&lt;/a&gt;.  The answers said to delete Time Machine local snapshots. I did so, and &lt;em&gt;boom&lt;/em&gt;, the Disk Utility app reported I had 100GB free. I tried the Archive Utility app again. This time it expanded the xip file and installed XCode Beta! Yay!&lt;/p&gt;

&lt;p&gt;The commands I used to list and delete the time machine files were (from &lt;a href="https://apple.stackexchange.com/a/352511/432763" rel="noopener noreferrer"&gt;this answer&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;tmutil listlocalsnapshots /
com.apple.TimeMachine.2021-08-20-195949.local
com.apple.TimeMachine.2021-09-08-114353.local

 &lt;span class="nv"&gt;$ &lt;/span&gt;tmutil deletelocalsnapshots 2021-08-20-195949
 &lt;span class="nv"&gt;$ &lt;/span&gt;tmutil listlocalsnapshots /
 com.apple.TimeMachine.2021-09-08-114353.local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why is there a discrepancy between the free space reported by the About This Mac and the Disk Utility apps? Because &lt;a href="https://support.apple.com/en-us/HT204015" rel="noopener noreferrer"&gt;macOS will automatically delete snapshots when space is needed&lt;/a&gt;. So, About This Mac considers the space used by local snapshots as available. Therefore, it reported ~100GB free space. However, the Disk Utility does &lt;strong&gt;not&lt;/strong&gt; consider snapshots as free space. The snapshots are using space so Disk Utility reports that space as used. So, it reported there was only ~25GB of free space.&lt;/p&gt;

&lt;p&gt;The Archive Utility also does &lt;strong&gt;not&lt;/strong&gt; consider the storage used by snapshot as free space. It checks for disk space before extracting the file and, in my case, detected that only ~25GB was free. By deleting the local snapshot, it was able to expand the xip file and install XCode Beta. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;References:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://apple.stackexchange.com/q/324440" rel="noopener noreferrer"&gt;macOS not showing the actual free space&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/geekculture/installing-xcode-with-not-enough-disk-space-available-b96c8f17115b" rel="noopener noreferrer"&gt;Installing Xcode with “not enough disk space available”&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.apple.com/en-us/HT204015" rel="noopener noreferrer"&gt;About Time Machine local snapshots&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.cnet.com/tech/computing/disk-utility-showing-less-available-space-than-the-finder-in-os-x/" rel="noopener noreferrer"&gt;Disk Utility showing less available space than the Finder in OS X&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>xcode</category>
      <category>ios</category>
    </item>
    <item>
      <title>Generating A New Apple Distribution Certificate </title>
      <dc:creator>Monica Granbois</dc:creator>
      <pubDate>Mon, 29 Mar 2021 19:05:33 +0000</pubDate>
      <link>https://dev.to/monicag/generating-a-new-apple-distribution-certificate-4cpb</link>
      <guid>https://dev.to/monicag/generating-a-new-apple-distribution-certificate-4cpb</guid>
      <description>&lt;p&gt;This post is a note to my future self about generating Apple Distribution Certificates. I received an email stating:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Your Distribution Certificate will no longer be valid in 30 days. To generate a new certificate, sign in and visit &lt;a href="https://developer.apple.com/account"&gt;Certificates, Identifiers &amp;amp; Profiles&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After signing in, the Certificates, Identifiers &amp;amp; Profiles page will list two certificates:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Apple Distribution certificate

&lt;ul&gt;
&lt;li&gt;Used to distribute apps to TestFlight and the App Store. &lt;/li&gt;
&lt;li&gt;An expired certificate does not affect my apps on the App Store. However, I cannot update my apps or upload new apps with the expired certificate. &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Apple Development certificate

&lt;ul&gt;
&lt;li&gt;Used to run apps on devices and use app services.
&lt;/li&gt;
&lt;li&gt;It will have my computer's name appended to the certificate name.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To generate a new certificate I followed the &lt;a href="https://stackoverflow.com/a/59850970/4704303"&gt;steps from this StackOverflow answer&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Open Xcode&lt;/li&gt;
&lt;li&gt;Open Xcode Preferences (Xcode-&amp;gt;Preferences or Cmd-,)&lt;/li&gt;
&lt;li&gt;Click on Accounts&lt;/li&gt;
&lt;li&gt;At the left, click on your developer ID&lt;/li&gt;
&lt;li&gt;At the bottom right, click on Manage Certificates...&lt;/li&gt;
&lt;li&gt;In the lower left corner, click the arrow to the right of the + (plus)&lt;/li&gt;
&lt;li&gt;Select Apple Distribution from the menu&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;I also revoked the old certificate on the &lt;a href="https://developer.apple.com/account/resources/certificates/list"&gt;Certificates, Identifiers &amp;amp; Profiles&lt;/a&gt; page. I did this by clicking on the distribution certificate and then clicking the revoke button.&lt;/p&gt;

&lt;p&gt;XCode manages my provisioning profile, so I did not need to manually update it.&lt;/p&gt;

&lt;p&gt;References:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/a/59850970/4704303"&gt;https://stackoverflow.com/a/59850970/4704303&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/support/certificates/"&gt;https://developer.apple.com/support/certificates/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://help.apple.com/xcode/mac/current/#/dev3a05256b8"&gt;https://help.apple.com/xcode/mac/current/#/dev3a05256b8&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>xcode</category>
      <category>ios</category>
      <category>certificate</category>
    </item>
    <item>
      <title>How To Use GitHub Actions To Deploy an 11ty Website To S3</title>
      <dc:creator>Monica Granbois</dc:creator>
      <pubDate>Thu, 11 Feb 2021 19:01:37 +0000</pubDate>
      <link>https://dev.to/monicag/how-to-use-github-actions-to-deploy-an-11ty-websiteite-to-s3-3jip</link>
      <guid>https://dev.to/monicag/how-to-use-github-actions-to-deploy-an-11ty-websiteite-to-s3-3jip</guid>
      <description>&lt;p&gt;I use &lt;a href="https://www.11ty.dev/"&gt;11ty&lt;/a&gt; to generate a static website and &lt;a href="https://aws.amazon.com/s3/"&gt;S3&lt;/a&gt; to host it. This post describes how I set up a workflow in &lt;a href="https://docs.github.com/en/actions"&gt;GitHub Actions&lt;/a&gt; to build and deploy the website. The workflow uses the AWS - CLI &lt;a href="https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3/sync.html"&gt;S3 - sync&lt;/a&gt; command to transfer the files. &lt;/p&gt;

&lt;p&gt;This post describes how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create an AWS policy with only those permission needed by the &lt;code&gt;sync&lt;/code&gt; command&lt;/li&gt;
&lt;li&gt;create an &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction.html"&gt;AWS individual IAM user&lt;/a&gt; with the above policy&lt;/li&gt;
&lt;li&gt;create a GitHub Action workflow using the &lt;code&gt;sync&lt;/code&gt; command to upload the files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I will provide the policy and workflow files first for those wanting only the code. A detailed walk-through is provided below these files. This post assumes you already have set up an S3 bucket. If not, please see the &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/WebsiteHosting.html"&gt;Hosting a static website using Amazon S3&lt;/a&gt; guide.  &lt;/p&gt;

&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;code&gt;s3_sync_policy.json&lt;/code&gt;&lt;/strong&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"VisualEditor0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"s3:PutObject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"s3:GetObject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"s3:ListBucket"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"s3:DeleteObject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"s3:GetBucketLocation"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:s3:::your-bucket-name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:s3:::your-bucket-name/*"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;code&gt;github_build_and_deploy_workflow.yml&lt;/code&gt;&lt;/strong&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and Deploy to S3&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build_and_deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2.3.4&lt;/span&gt;

      &lt;span class="c1"&gt;# Uncomment if you want to specify a certain &lt;/span&gt;
      &lt;span class="c1"&gt;# Node version. Otherwise the Node version installed&lt;/span&gt;
      &lt;span class="c1"&gt;# on the GitHub VM will be used. For more details&lt;/span&gt;
      &lt;span class="c1"&gt;# see: https://github.com/actions/virtual-environments &lt;/span&gt;
      &lt;span class="c1"&gt;# - name: Setup Node.js environment&lt;/span&gt;
      &lt;span class="c1"&gt;#   uses: actions/setup-node@v2.1.4&lt;/span&gt;
      &lt;span class="c1"&gt;#   with:&lt;/span&gt;
      &lt;span class="c1"&gt;#     node-version: '15.7.0'&lt;/span&gt;

      &lt;span class="c1"&gt;# Uncomment if your project uses dependencies&lt;/span&gt;
      &lt;span class="c1"&gt;# - name: Install dependencies&lt;/span&gt;
      &lt;span class="c1"&gt;#   run: npm ci&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build the website&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npx @11ty/eleventy&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Configure AWS Credentials&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-actions/configure-aws-credentials@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;aws-access-key-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AWS_ACCESS_KEY_ID }}&lt;/span&gt;
          &lt;span class="na"&gt;aws-secret-access-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AWS_SECRET_ACCESS_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;aws-region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-west-2&lt;/span&gt; &lt;span class="c1"&gt;# replace this with your aws-region&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Upload files to S3 with AWS CLI&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;aws s3 sync _site/ s3://${{ secrets.S3_BUCKET }} --delete &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Walk-through
&lt;/h2&gt;

&lt;p&gt;There are many menus to navigate to set this up. I've created a video walk through to help explain where everything is. If you want just the text write up then please skip to the AWS Setup section below.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/h-iowIY4DCU"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  AWS Setup
&lt;/h3&gt;

&lt;p&gt;Using an individual IAM account with limited permissions is a best practice. Creating a new user, rather than using an existing one, means the credentials are not shared across services. If the credentials change then GitHub alone will be affected. Plus, we can restrict the user to only those permissions needed by the workflow. In this case, the account will only have permissions for the actions needed by the &lt;code&gt;sync&lt;/code&gt; command. For more details on credential best practices, see the &lt;a href="https://github.com/marketplace/actions/configure-aws-credentials-action-for-github-actions#credentials"&gt;configure-aws-credentials&lt;/a&gt; documentation.&lt;/p&gt;

&lt;h4&gt;
  
  
  Create a policy
&lt;/h4&gt;

&lt;p&gt;The first step is to create a policy that will only contain the permissions needed by the &lt;code&gt;sync&lt;/code&gt; command. This policy will be used during the account creation step. &lt;/p&gt;

&lt;p&gt;To create the policy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sign in to the &lt;a href="https://console.aws.amazon.com/iam/"&gt;IAM Console&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Goto Access Management -&amp;gt; Policies -&amp;gt; Create Policy.&lt;/li&gt;
&lt;li&gt;Click the JSON tab and paste in the JSON policy from above.&lt;/li&gt;
&lt;li&gt;Click the 'Review' Policy button.&lt;/li&gt;
&lt;li&gt;Next, name the policy. I used S3-Sync-Policy. &lt;/li&gt;
&lt;li&gt;Click the 'Create Policy' button.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This policy does the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allows the following actions:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;s3:PutObject&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;"Grants permission to add an object to a bucket"&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;s3:GetObject&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;"Grants permission to retrieve objects from Amazon S3"&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;s3:ListBucket&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;"Grants permission to list some or all of the objects in an Amazon S3 bucket"&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;s3:DeleteObject&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;"Grants permission to remove the null version of an object and insert a delete marker, which becomes the current version of the object"&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;s3:GetBucketLocation&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;Grants permission to return the Region that an Amazon S3 bucket resides in"&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Specifies the resources the actions can be applied to. Ensure you change the &lt;code&gt;your-bucket-name&lt;/code&gt; value.

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;arn:aws:s3:::your-bucket-name&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Applied to the bucket: &lt;code&gt;your-bucket-name&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;arn:aws:s3:::your-bucket-name/*&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Applied to all objects in &lt;code&gt;your-bucket-name&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also create the policy using the Visual Editor rather than pasting in the JSON code.  Note: You do not need to do both. The Visual Editor is a second way to create the policy. The video demonstrates using the Visual Editor.&lt;/p&gt;

&lt;h4&gt;
  
  
  Create a user
&lt;/h4&gt;

&lt;p&gt;The next step is to create a user and apply the policy to it.&lt;/p&gt;

&lt;p&gt;To create a user:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Goto Access Management -&amp;gt; Users -&amp;gt; Add user&lt;/li&gt;
&lt;li&gt;On the first screen:

&lt;ul&gt;
&lt;li&gt;Enter a name in the "User Name" field. I chose github-actions.&lt;/li&gt;
&lt;li&gt;Select "Programmatic access" in the "Access Type" section. This user does not need "AWS Management Console access". &lt;/li&gt;
&lt;li&gt;Click the "Next: Permissions" button.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;On the second screen:

&lt;ul&gt;
&lt;li&gt;Click on the "Attach existing policies directly" box in the "Set permissions" section.&lt;/li&gt;
&lt;li&gt;Use the search feature to find the policy created in the previous section. Select it.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4lKtXgLR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://monicagranbois.com/images/github-actions/iam-create-user-filter-policy.png" alt="The IAM Add User Permission screen. The search feature has been used to find the policy created previously." width="880" height="285"&gt;
&lt;/li&gt;
&lt;li&gt;Click the "Next: Tags" button.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;On the third screen:

&lt;ul&gt;
&lt;li&gt;Optionally add a tag (or more) for the user.&lt;/li&gt;
&lt;li&gt;Click the "Next: Review" button.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;On the fourth screen:

&lt;ul&gt;
&lt;li&gt;Review the user settings.&lt;/li&gt;
&lt;li&gt;Click the "Create User" button.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;On the fifth screen:

&lt;ul&gt;
&lt;li&gt;This screen displays the user's AWS credentials. These will be used with the GitHub repo. WARNING: This is the only time the credentials will be available to download. Either keep this screen open while setting up GitHub, OR click the Download button.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  GitHub Setup
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Setup AWS Credentials
&lt;/h4&gt;

&lt;p&gt;The GitHub action will need the AWS credentials in order to work. Add the credentials to your repo by doing the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to your repo&lt;/li&gt;
&lt;li&gt;Click Settings -&amp;gt; Secrets -&amp;gt; New Repository Secret&lt;/li&gt;
&lt;li&gt;Using the credentials created in the previous section, add keys for:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AWS_ACCESS_KEY_ID&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AWS_SECRET_ACCESS_KEY&lt;/code&gt; &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Also, add an &lt;code&gt;S3_BUCKET&lt;/code&gt; key with the name of your S3 bucket.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Create a GitHub Workflow
&lt;/h4&gt;

&lt;p&gt;Now to create a workflow that will build the website and upload it to S3.  The workflow will do the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use the &lt;a href="https://github.com/marketplace/actions/configure-aws-credentials-action-for-github-actions"&gt;configure-aws-credentials&lt;/a&gt; to access the AWS access and secret keys&lt;/li&gt;
&lt;li&gt;Build the website using 11ty&lt;/li&gt;
&lt;li&gt;Use the &lt;a href="https://aws.amazon.com/cli/"&gt;AWS-CLI&lt;/a&gt; to upload the generated site to the S3 bucket&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To create the workflow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In your repo click the Actions link.&lt;/li&gt;
&lt;li&gt;Click on the "Skip this and set up a workflow yourself" link.
&lt;/li&gt;
&lt;li&gt;Remove the template code and replace it with the yml file from above.&lt;/li&gt;
&lt;li&gt;Change the &lt;code&gt;aws-region&lt;/code&gt; location to your bucket's region.&lt;/li&gt;
&lt;li&gt;Optionally, rename the file.&lt;/li&gt;
&lt;li&gt;Click the "Start Commit" button.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Below is a description of what each line does:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and Deploy to S3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The name of this workflow. It will show under the Actions tab in your repository. Name the workflow whatever you like.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This is the event that will trigger this workflow. In this example, it will run on any push to the repository. See &lt;a href="https://docs.github.com/en/actions/reference/events-that-trigger-workflows"&gt;Events that trigger workflows&lt;/a&gt; for other options.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Groups together the jobs that run in this workflow (Build and Deploy to S3).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;build_and_deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The name of the job. This name can be whatever you would like it to be.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In this example, the workflow will run in an ubuntu virtual environment using the latest version supported by GitHub. See &lt;a href="https://docs.github.com/en/actions/reference/specifications-for-github-hosted-runners"&gt;Specifications for GitHub-hosted runners&lt;/a&gt; for other options.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Groups together all the steps that run during the build_and_deploy job.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  &lt;span class="s"&gt;Checkout repository`&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2.3.4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This block of code is one step. The &lt;code&gt;name&lt;/code&gt; is optional. If provided it will be displayed in the output when the workflow runs. The &lt;code&gt;uses&lt;/code&gt; keyword retrieves the action to run in this step. An action is a reusable piece of code. In this case, the step uses the public action actions/&lt;a href="mailto:checkout@v2.3.4"&gt;checkout@v2.3.4&lt;/a&gt; to checkout your repository. See the &lt;a href="https://github.com/marketplace/actions/checkout"&gt;Checkout&lt;/a&gt; documentation for more details.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt; &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build the website&lt;/span&gt;
   &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npx @11ty/eleventy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This step uses the &lt;code&gt;run&lt;/code&gt; keyword to execute a command-line program using the OS's default shell. In this case, &lt;code&gt;npx&lt;/code&gt; is used to generate the 11ty website.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Configure AWS Credentials&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-actions/configure-aws-credentials@v1&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;aws-access-key-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AWS_ACCESS_KEY_ID }}&lt;/span&gt;
    &lt;span class="na"&gt;aws-secret-access-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AWS_SECRET_ACCESS_KEY }}&lt;/span&gt;
    &lt;span class="na"&gt;aws-region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-west-2&lt;/span&gt; &lt;span class="c1"&gt;# replace this with your aws-region&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This step uses the public &lt;code&gt;configure-aws-credentials&lt;/code&gt; action to set up the credentials for the workflow. The &lt;code&gt;with&lt;/code&gt; keyword is a map of input parameters to the action. Here, the repo's AWS secret keys are passed to the action. See &lt;a href="https://github.com/marketplace/actions/configure-aws-credentials-action-for-github-actions#credentials"&gt;"Configure AWS Credentials" Action For GitHub Actions&lt;/a&gt; for more details on this Action.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt; &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Upload files to S3 with AWS CLI&lt;/span&gt;
   &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
     &lt;span class="s"&gt;aws s3 sync _site/ s3://${{ secrets.S3_BUCKET }} --delete &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This step uses the AWS CLI to transfer the files generated by the "Build the website" step to the S3 bucket.  The AWS CLI is installed by default on the virtual machine. See the &lt;a href="https://github.com/actions/virtual-environments"&gt;GitHub Actions Virtual Environments&lt;/a&gt; for more details on pre-installed tools.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3/sync.html"&gt;S3 sync&lt;/a&gt; command recursively copies files from the &lt;code&gt;_site&lt;/code&gt; directory to the S3 bucket. The &lt;code&gt;--delete&lt;/code&gt; option deletes any files in the S3 bucket that are not in the &lt;code&gt;_site&lt;/code&gt; directory. &lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Run the workflow
&lt;/h4&gt;

&lt;p&gt;The workflow will run when a commit is pushed to the repo.  You can see the output of a run by going to the 'Actions' menu for the repo. Here is an example of the output of the workflow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--v3MnNEbR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://monicagranbois.com/images/github-actions/output.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--v3MnNEbR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://monicagranbois.com/images/github-actions/output.png" alt="The output after a workflow has been executed. It shows the steps run." width="880" height="310"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thank you for reading! I hope you found this useful!&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction.html"&gt;AWS Identity and Access Management User Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/q/48894886/4704303"&gt;AWS permissions required for sync&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3/sync.html"&gt;S3 - sync&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/actions"&gt;GitHub Actions &lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>tutorial</category>
      <category>github</category>
      <category>aws</category>
      <category>11ty</category>
    </item>
    <item>
      <title>Autoscaling PDF Images On Apple watchOS</title>
      <dc:creator>Monica Granbois</dc:creator>
      <pubDate>Thu, 26 Nov 2020 17:14:53 +0000</pubDate>
      <link>https://dev.to/monicag/autoscaling-pdf-images-on-apple-watchos-2h7c</link>
      <guid>https://dev.to/monicag/autoscaling-pdf-images-on-apple-watchos-2h7c</guid>
      <description>&lt;p&gt;In this post, I will discuss autoscaling PDF images and how to add one to a watchOS project.  &lt;/p&gt;

&lt;p&gt;The PDF format allows an image to scale without looking fuzzy at different sizes. This format is best used with vector artwork; think icons. Programs like &lt;a href="https://inkscape.org/"&gt;Inkscape&lt;/a&gt;, &lt;a href="https://www.adobe.com/products/illustrator.html"&gt;Adobe Illustrator&lt;/a&gt; and, &lt;a href="https://www.vectornator.io/"&gt;Vectornator&lt;/a&gt; can export PDF images.&lt;/p&gt;

&lt;p&gt;For more details on designing a PDF image see the &lt;a href="https://developer.apple.com/design/human-interface-guidelines/watchos/visual/image-optimization/autoscaling-pdf-images"&gt;Image Optimization&lt;/a&gt; page in the &lt;a href="https://developer.apple.com/design/human-interface-guidelines/watchos/overview/getting-started/"&gt;Human Interface Guidelines for watchOS&lt;/a&gt;. It includes the scales used for the different screen sizes.&lt;/p&gt;

&lt;p&gt;Setting certain flags in XCode will tell WatchKit to scale the image based on screen size. And so, you have an autoscaling PDF image. This means the project only needs one image for all screen sizes. Otherwise, the project would need multiple scaled image files.&lt;/p&gt;

&lt;p&gt;Once you have a PDF image, add it to the Asset Catalog in an XCode project.  &lt;/p&gt;

&lt;p&gt;In an XCode project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to Assets.xcassets&lt;/li&gt;
&lt;li&gt;Right-click anywhere in the Asset Catalog Editor (the white area). This will bring up a pop-up menu.&lt;/li&gt;
&lt;li&gt;Choose Image Set. This will create an empty Image Set. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pLhoLMkL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://monicagranbois.com/images/autoscalingPDFImage/createImageSet.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pLhoLMkL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://monicagranbois.com/images/autoscalingPDFImage/createImageSet.png" alt="Image showing Assets.xcassets pop-up menu" width="880" height="569"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next step is to change some settings in the Attribute Inspector. If the Inspectors editor is not displayed, open it using the button in the top right corner of XCode.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eNA3bU4w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://monicagranbois.com/images/autoscalingPDFImage/inspectorToggle.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eNA3bU4w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://monicagranbois.com/images/autoscalingPDFImage/inspectorToggle.png" alt="Image showing the inspector toggle." width="364" height="134"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, mark the image for use on an Apple Watch. In the 'Devices' section of the Attribute Inspector:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;uncheck the 'Universal' option&lt;/li&gt;
&lt;li&gt;check the 'Apple Watch' option&lt;/li&gt;
&lt;li&gt;This will result in one 2x square for the Apple Watch.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KxN0NZfg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://monicagranbois.com/images/autoscalingPDFImage/appleWatchImageSet.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KxN0NZfg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://monicagranbois.com/images/autoscalingPDFImage/appleWatchImageSet.png" alt="Image showing the settings for an Apple Watch Image Set" width="880" height="202"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now add the pdf image to XCode. I'm using this &lt;a href="https://monicagranbois.com/images/autoscalingPDFImage/happy.pdf"&gt;happy face image&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Drag and drop the image onto the 2x Apple Watch Square.&lt;/li&gt;
&lt;li&gt;Give the Image Set a name in the Attribute Inspector. I chose 'Happy'.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7PfKMxeX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://monicagranbois.com/images/autoscalingPDFImage/happyImageAdded2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7PfKMxeX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://monicagranbois.com/images/autoscalingPDFImage/happyImageAdded2.png" alt="Image showing the happy face pdf image added to Image Set" width="880" height="186"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next set the scaling options in the Attribute Inspector:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set the 'Scales' option to 'Single Scale'&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dV1Dn2s3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://monicagranbois.com/images/autoscalingPDFImage/singleScale.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dV1Dn2s3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://monicagranbois.com/images/autoscalingPDFImage/singleScale.png" alt="Image showing the scaling options" width="524" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Further down the menu, in the 'Apple Watch' section, change 'Auto Scaling' to 'Automatic'&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xccze3cH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://monicagranbois.com/images/autoscalingPDFImage/autoScale.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xccze3cH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://monicagranbois.com/images/autoscalingPDFImage/autoScale.png" alt="Image showing the Apple Watch options" width="524" height="160"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, the image will scale based on the Apple Watch's screen size!&lt;/p&gt;

&lt;p&gt;An aside about the ‘Preserve Vector Data’ box in the Attribute Inspector. This appears to be for iOS apps and not watchOS apps. Checking the box means the vector data is included with an iOS app. This will allow the  image to scale automatically. The option is discussed in the &lt;a href="https://developer.apple.com/videos/play/wwdc2017/201/?time=2034"&gt;What's New in Cocoa Touch&lt;/a&gt; video around the 33:50 mark.&lt;/p&gt;

&lt;p&gt;I experimented with both checking and unchecking  the box. It did not seem to make a difference for a watchOS project. I created a &lt;a href="https://stackoverflow.com/q/64668198/4704303"&gt;StackOverflow Question&lt;/a&gt; about it, but as of this writing, it has not received a response.  I have left the ‘Preserve Vector Data' box unchecked.&lt;/p&gt;

&lt;p&gt;Below is code demonstrating the use of the autoscaling PDF image. The result shown is from the preview canvas for 44mm and 38mm screens.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;SwiftUI&lt;/span&gt;

&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;ContentView&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;View&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;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;VStack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="kt"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Happy"&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="s"&gt;"Hello, World!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;padding&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="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;ContentView_Previews&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PreviewProvider&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;previews&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="k"&gt;return&lt;/span&gt; &lt;span class="kt"&gt;Group&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;ContentView&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;previewDevice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Apple Watch Series 6 - 44mm"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kt"&gt;ContentView&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
          &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;previewDevice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Apple Watch Series 3 - 38mm"&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;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MVHCoYts--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://monicagranbois.com/images/autoscalingPDFImage/scaledImages2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MVHCoYts--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://monicagranbois.com/images/autoscalingPDFImage/scaledImages2.png" alt="Image showing the scaled Happy Face Image in the Preview Canvas on a 44mm and a 38mm screens." width="448" height="778"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>swift</category>
      <category>xcode</category>
      <category>watchos</category>
    </item>
    <item>
      <title>How I replaced a nested for loop with flatMap</title>
      <dc:creator>Monica Granbois</dc:creator>
      <pubDate>Wed, 04 Nov 2020 19:05:09 +0000</pubDate>
      <link>https://dev.to/monicag/how-i-replaced-a-nested-for-loop-with-flatmap-4ih0</link>
      <guid>https://dev.to/monicag/how-i-replaced-a-nested-for-loop-with-flatmap-4ih0</guid>
      <description>&lt;p&gt;This is a short post about how I refactored a nested for loop using flatMap. I had a struct that defined name and quantity properties as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;Item&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="kt"&gt;String&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;quantity&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The actual code used a custom type rather than a String, but I've used String here to simplify the example. &lt;/p&gt;

&lt;p&gt;I also had an array of Items. However, I wanted to create a new array that contained each Item's name for a count of its quantity. So, given the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;myList&lt;/span&gt; &lt;span class="o"&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Apple"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;quantity&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="kt"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Banana"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;quantity&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;Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Carrot"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;quantity&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;Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Doughnut"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;quantity&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="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I wanted a new String array containing the following:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;["Apple", "Apple", "Banana", "Carrot", "Carrot", "Carrot", "Doughnut"]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;My first attempt was using for loops:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;nameList&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="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;myList&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;nameList&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&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="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="n"&gt;nameList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, I did not like the nested for loops.  It seemed verbose; I wanted to condense the code. Fortunately, Swift provides two things that make this refactoring possible:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;An Array initializer that takes repeating and count parameters: &lt;a href="https://developer.apple.com/documentation/swift/array/1641692-init"&gt;Array(repeating:count:)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://developer.apple.com/documentation/swift/array/3126947-flatmap"&gt;flatMap&lt;/a&gt; function&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I thought of using &lt;a href="https://developer.apple.com/documentation/swift/array/3017522-map"&gt;map&lt;/a&gt;, but map would result in an array of arrays. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;nameListMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;myList&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;repeating&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$0&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="nv"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;)&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="n"&gt;nameListMap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;[["Apple", "Apple"], ["Banana"], ["Carrot", "Carrot", "Carrot"], ["Doughnut"]]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not what I wanted. Fortunately, there is flatMap. FlatMap works like the map function, but it flattens an array of arrays. Just what I needed! So, the for loop could be replaced with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;nameList2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;myList&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flatMap&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;repeating&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$0&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="nv"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quantity&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="n"&gt;nameList2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;["Apple", "Apple", "Banana", "Carrot", "Carrot", "Carrot", "Doughnut"]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The above code is doing the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;For each Item in myList a new Array is created. It is populated with the Item's name for a count specified by the Item's quantity.&lt;/li&gt;
&lt;li&gt;It then joins the arrays, resulting in a single, flat, array&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Hurray! &lt;/p&gt;

</description>
      <category>swift</category>
      <category>programming</category>
      <category>ios</category>
    </item>
    <item>
      <title>Looking for UI feedback</title>
      <dc:creator>Monica Granbois</dc:creator>
      <pubDate>Tue, 03 Nov 2020 19:23:12 +0000</pubDate>
      <link>https://dev.to/monicag/looking-for-ui-feedback-1dc8</link>
      <guid>https://dev.to/monicag/looking-for-ui-feedback-1dc8</guid>
      <description>&lt;p&gt;I would like some feedback on a screen I redesigned, please. I have a standalone Apple Watch app, &lt;a href="https://barbellhelper.com/"&gt;Barbell Helper&lt;/a&gt;. I updated the 'reverse calculator' screen. The purpose of the screen is to sum the weight of a barbell and the plates added to it. I changed the screen to provide a graphic of a barbell on which plates can be added or removed. &lt;/p&gt;

&lt;p&gt;I would appreciate it if you tried the app out and let me know your thoughts. The app is available on &lt;a href="https://testflight.apple.com/join/s0jqslOG"&gt;test flight&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here is a video of what the screen used to look like:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/IZDsL7OXu6o"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Here is a video of what the screen now looks like:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/0ILp7DWyW1w"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;I'm specifically looking for feedback on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;the ease of tapping the buttons to add a plate to the barbell&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the ease of tapping a plate on the barbell to remove it&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the ease of understanding what plates are in use and the total weight&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other feedback is also appreciated.&lt;/p&gt;

&lt;p&gt;Thank you!&lt;/p&gt;

</description>
      <category>ux</category>
      <category>help</category>
      <category>feedback</category>
      <category>discuss</category>
    </item>
    <item>
      <title> SwiftUI&amp;#58; Previewing localized text in the canvas</title>
      <dc:creator>Monica Granbois</dc:creator>
      <pubDate>Mon, 29 Jun 2020 23:32:16 +0000</pubDate>
      <link>https://dev.to/monicag/swiftui-58-previewing-localized-text-in-the-canvas-2gbm</link>
      <guid>https://dev.to/monicag/swiftui-58-previewing-localized-text-in-the-canvas-2gbm</guid>
      <description>&lt;p&gt;Last week I watched the &lt;a href="https://developer.apple.com/videos/play/wwdc2020/10169/"&gt;Swift packages: Resources and localization&lt;/a&gt; video from &lt;a href="https://developer.apple.com/wwdc20/"&gt;WWDC20&lt;/a&gt;. One thing I learned was that the Canvas can preview localized values. This is done by adding a locale to a View via the &lt;a href="https://developer.apple.com/documentation/swiftui/environment"&gt;environment&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;SwiftUI&lt;/span&gt;

&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;ContentView&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;View&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;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;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"greeting"&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="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;ContentView_Previews&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PreviewProvider&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;previews&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;ContentView&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;(\&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Locale&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;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"fr"&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 above code assumes that a French &lt;code&gt;Localizable.strings&lt;/code&gt; file exist with a &lt;code&gt;greeting&lt;/code&gt; key.  For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"greeting" = "Bonjour le monde!";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://developer.apple.com/documentation/swiftui/environmentvalues"&gt;EnvironmentValues&lt;/a&gt; documentation lists all the values that can be set.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xtWRhg7J--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/zjbaz0l42p6m64uc7hyy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xtWRhg7J--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/zjbaz0l42p6m64uc7hyy.gif" alt="Gif showing the Canvas preview switching greeting text between english, french and german." width="880" height="604"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ios</category>
      <category>swiftui</category>
    </item>
    <item>
      <title>How I debugged a crashing watchOS app with crash reports and log files</title>
      <dc:creator>Monica Granbois</dc:creator>
      <pubDate>Fri, 08 May 2020 18:03:01 +0000</pubDate>
      <link>https://dev.to/monicag/how-i-debugged-a-crashing-watchos-app-with-crash-reports-and-log-files-31jg</link>
      <guid>https://dev.to/monicag/how-i-debugged-a-crashing-watchos-app-with-crash-reports-and-log-files-31jg</guid>
      <description>&lt;p&gt;In a &lt;a href="https://dev.to/monicag/rejection-acceptance-and-crashes-my-testflight-journey-4lop"&gt;previous blog post&lt;/a&gt;, I wrote about my app crashing when it was installed via TestFlight.  In this blog post, I will go into more detail about how I debugged the problem using logs and crash reports.&lt;/p&gt;

&lt;p&gt;First, a quick recap of the problem.  I wrote a stand-alone watchOS app.  The app worked when installed via Xcode to my Apple Watch.  However, it crashed when installed via TestFlight.  This happened on both my and my husband’s watches. &lt;/p&gt;

&lt;p&gt;My first step was to get the logs.  In my case, there were two log files I wanted to look at.  One was the log messages my app created using the &lt;a href="https://developer.apple.com/documentation/os/logging"&gt;unified logging system&lt;/a&gt;.  The other was the crash report.&lt;/p&gt;

&lt;p&gt;I installed &lt;a href="https://developer.apple.com/bug-reporting/profiles-and-logs/?platform=watchos&amp;amp;name=sysdiagnose"&gt;sysdiagnose&lt;/a&gt; on my watch to see the unified logging messages. I followed &lt;a href="https://download.developer.apple.com/iOS/watchOS_Logs/sysdiagnose_Logging_Instructions.pdf"&gt;these instructions&lt;/a&gt; (you will need to be logged into your apple developer account to view these instructions) to create a &lt;code&gt;co-sysdiagnose&lt;/code&gt; file and copy it to my computer.  The co-sysdiagnose tar file contains two tar.gz files, one for the iPhone and one for the watch. The watch file will have a name like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; sysdiagnose_YEAR.MONTH.DAY_HH-MM-SS-xxxx_Watch-OS_Watch_watchOSBuildNumber.tar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  sysdiagnose_2020.04.30_09-06-08-0700_Watch-OS_Watch_17T530.tar.gz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://www.howtogeek.com/362203/what-is-a-tar.gz-file-and-how-do-i-open-it/"&gt;extracted archive&lt;/a&gt; contains many files and directories.  The file that contains the log messages is &lt;code&gt;system_logs.logarchive&lt;/code&gt; located at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sysdiagnose_YEAR.MONTH.DAY_HH-MM-SS-xxxx_Watch-OS_Watch_watchOSBuildNumber/system_logs.logarchive
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file can be opened in the Console app (located on your computer in Applications -&amp;gt; Utilities).  &lt;/p&gt;

&lt;p&gt;All info and debug messages will be shown automatically.  Usually, when using the Console app you would need to select these options under the Action menu. There were a lot of log messages displayed. These are from various systems on the Apple Watch. To filter the log messages so I could see just mine, I entered the &lt;a href="https://developer.apple.com/documentation/os/logger/3551621-init"&gt;subsystem&lt;/a&gt; my log messages used.&lt;/p&gt;

&lt;p&gt;The crash report is also located in the extracted archive at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; sysdiagnose_YEAR.MONTH.DAY_HH-MM-SS-xxxx_Watch-OS_Watch_watchOSBuildNumber/crashes_and_spins/app_name.ips.beta
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Crash reports are also available when an app is distributed through TestFlight.  This is not the case when an app is released on the App Store. In that case, the user needs to opt in to sending crash reports and statistics to the developer.  To download a crash report go to Xcode -&amp;gt; Windows -&amp;gt; Organizer and click on the crashes tab. However, &lt;a href="https://help.apple.com/xcode/mac/current/#/dev861f46ea8"&gt;it can take up to a day for the reports to be available.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Crash reports can also be copied from the device to your computer.  On the iPhone go to the Settings app -&amp;gt; Privacy -&amp;gt; Analytics &amp;amp; Improvements -&amp;gt; Analytics Data to see the files.&lt;/p&gt;

&lt;p&gt;It can take a while for the files to sync from the watch to the phone.  If the crash report is not on the phone, then it is likely still on the watch.  Go to the Watch app on the iPhone and then General -&amp;gt; Diagnostic Logs to see the crash report.&lt;/p&gt;

&lt;p&gt;In both locations, the crash report can be shared via the standard share icon.  I use AirDrop to transfer files to my computer.&lt;/p&gt;

&lt;p&gt;Regardless of how the crash report was obtained, the debug symbols need to be downloaded.  In Xcode go to Window -&amp;gt; Organizer and click on the “Download Debug Symbols” button. It is on the right-hand side of the screen for each archive. Note: This is dependant upon the symbols having been submitted when uploading the app's archive to TestFlight. This is done by checking the "Upload your app's symbols to receive symbolication reports from Apple" option when submitting the Archive.&lt;/p&gt;

&lt;p&gt;The debug symbols translate the hexadecimal&lt;br&gt;
addresses of the thread backtraces into function names and line numbers.  This makes the crash report readable by humans.&lt;/p&gt;

&lt;p&gt;Opening the crash report takes a few steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Rename the file to have a .crash extension. `app_name.ips.beta -&amp;gt; app_name.ips.beta.crash’&lt;/li&gt;
&lt;li&gt; Go to Window -&amp;gt; Devices and Simulators&lt;/li&gt;
&lt;li&gt;Select the connected device and then click on the View Device Logs button. (The iPhone will need to be attached to the computer either by USB cable or &lt;a href="https://help.apple.com/xcode/mac/11.0/index.html?localePath=en.lproj#/devbc48d1bad"&gt;have wireless enabled&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Drag the `app_name.ips.beta.crash’ into the list of log files&lt;/li&gt;
&lt;li&gt;See &lt;a href="https://developer.apple.com/documentation/xcode/diagnosing_issues_using_crash_reports_and_device_logs/adding_identifiable_symbol_names_to_a_crash_report"&gt;Adding Identifiable Symbol Names to a Crash Report&lt;/a&gt; for more details.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I looked at the crash report and saw the app crashed when it decoded the contents of a plist file.  The log messages from my app showed the same thing.  The app logged it was about to decode the file and then no further messages.&lt;/p&gt;

&lt;p&gt;Here is an excerpt of the relevant part of the crash report:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_INVALID_ADDRESS at 0x00000000
VM Region Info: 0 is not in any region.  Bytes before following region: 3686400
      REGION TYPE              START - END     [ VSIZE] PRT/MAX SHRMOD  REGION DETAIL
      UNUSED SPACE AT START
--------&amp;gt;  
      __TEXT                 00384000-0039c000 [   96K] r-x/r-x SM=COW  ...Kit Extension

Termination Signal: Segmentation fault: 11
Termination Reason: Namespace SIGNAL, Code 0xb
Terminating Process: exc handler [384]
Triggered by Thread:  0


Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0   libswiftCore.dylib              0x659e0e06 swift_checkMetadataState + 14
1   libswiftCore.dylib              0x659b66a8 type metadata completion function for ClosedRange&amp;lt;&amp;gt;.Index + 14
2   libswiftCore.dylib              0x659dc504 swift_getGenericMetadata + 1112
3   libswiftCore.dylib              0x659b5644 __swift_instantiateGenericMetadata + 28
4   libswiftFoundation.dylib        0x65b80fb0 PropertyListDecoder.decode&amp;lt;A&amp;gt;+ 651184 (_:from:format:) + 310
5   libswiftFoundation.dylib        0x65b80e72 PropertyListDecoder.decode&amp;lt;A&amp;gt;+ 650866 (_:from:) + 40
6   libswiftFoundation.dylib        0x65bf9b2c dispatch thunk of PropertyListDecoder.decode&amp;lt;A&amp;gt;+ 1145644 (_:from:) + 28
7   ...llHelper WatchKit Extension  0x0038ddb6 specialized load&amp;lt;A&amp;gt;(_:) + 40374 (Data.swift:117)

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

&lt;/div&gt;



&lt;p&gt;The last line, Data.swift: 117 refers to the Data.swift file in my app.  It was crashing at line 117.&lt;/p&gt;

&lt;p&gt;The code in question was the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;decoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;PropertyListDecoder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="n"&gt;decoder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;T&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;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;-- this is the line it crashed at&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html"&gt;struct&lt;/a&gt; the app was trying to decode the plist file into lives in a &lt;a href="https://swift.org/package-manager/"&gt;Swift Package&lt;/a&gt;. It is in a Swift Package because unit testing is not available in watchOS projects.  However, unit testing is available in Swift Packages. So, my models and business logic are there. &lt;/p&gt;

&lt;p&gt;I wasn’t sure what was causing the error. Maybe it was something with my code; was I using the Codable protocol properly? Maybe the plist file permissions were different between Xcode and TestFlight? Maybe it had something to do with the Swift Package?  &lt;/p&gt;

&lt;p&gt;I decided to move my struct from the Swift Package into the app project.  I figured this way I could rule out if the Swift Package was an issue.  If the app still crashed then there was something wrong with my code or file access.  If the app did not crash then there was something wrong with how I was using the Swift Package.&lt;/p&gt;

&lt;p&gt;Before I changed or moved any code I created a git branch to work on.  I copied the struct to the app codebase.  I then removed the swift package from the app.  This, of course, caused many build errors.  To get around this I deleted files en masse. This left me with just the code that was causing the error.  &lt;/p&gt;

&lt;p&gt;The code still worked in the simulator and on my device, so I uploaded it to TestFlight.  This time the app worked when I installed it from TestFlight.&lt;/p&gt;

&lt;p&gt;This meant there was something wrong with how I was using the Swift Package.  I checked my code to ensure I used the proper &lt;a href="https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html"&gt;access levels&lt;/a&gt; with my struct. Code in a Swift Package requires a public access level, otherwise, the calling code cannot access it. But, my code was fine.&lt;/p&gt;

&lt;p&gt;I was at a loss at this point. I did google searches but had no luck.  I created a &lt;a href="https://stackoverflow.com/questions/61529502/swift-propertylistdecoder-not-working-on-type-from-swift-package"&gt;Stack Overflow question&lt;/a&gt;. The process of writing down my problem helped clarify my thoughts. With these new ideas, I did more research and found this &lt;a href="https://stackoverflow.com/questions/58801669/testflight-installed-app-crash-with-swift-package-manager-dependencies/58948017#58948017"&gt;Stack Overflow answer&lt;/a&gt; which solved my problem.  I had to set &lt;code&gt;DEAD_CODE_STRIPPING = NO&lt;/code&gt; in the build settings of my app.&lt;/p&gt;

&lt;p&gt;I delete the struct from the app code and imported the Swift Package again. This time when I installed the app from TestFlight it worked.  At this point, I switched back to my main git branch and changed the build settings.  I uploaded it to TestFlight and everything worked.&lt;/p&gt;

&lt;p&gt;I am grateful that people post solutions online. I would never have found the &lt;code&gt;DEAD_CODE_STRIPPING = NO&lt;/code&gt; workaround on my own.&lt;/p&gt;

&lt;p&gt;Thank you for reading! I hope you found it helpful!  If you have some time I would appreciate feedback on my app, &lt;a href="https://barbellhelper.com/"&gt;Barbell Helper&lt;/a&gt;, available on &lt;a href="https://testflight.apple.com/join/s0jqslOG"&gt;TestFlight&lt;/a&gt;. Thank you!&lt;/p&gt;

</description>
      <category>swift</category>
      <category>ios</category>
      <category>watchos</category>
      <category>debugging</category>
    </item>
    <item>
      <title>Rejection, Acceptance, And Crashes. My TestFlight Journey</title>
      <dc:creator>Monica Granbois</dc:creator>
      <pubDate>Sun, 03 May 2020 15:33:16 +0000</pubDate>
      <link>https://dev.to/monicag/rejection-acceptance-and-crashes-my-testflight-journey-4lop</link>
      <guid>https://dev.to/monicag/rejection-acceptance-and-crashes-my-testflight-journey-4lop</guid>
      <description>&lt;p&gt;I hit a small milestone recently, I submitted my first app to TestFlight.  I thought this process would be quick.  Maybe a day or two.  A week later I finally had a working app on TestFlight.  During that time I learned much about crash logs and debugging. Here is my story.&lt;/p&gt;

&lt;p&gt;I’ve been working on a stand-alone     Apple Watch app.  It is a weight plate calculator for the gym.  The idea is you enter the amount you want to lift along with the barbell you are using. The app will then tell you which weight plates to use. &lt;/p&gt;

&lt;p&gt;I started this project before the pandemic quarantines.  When the quarantines started I thought about abandoning the project.  Gyms are closed. Who is going to use this app?  But I decided to carry on.  I liked working on it. Plus, one day the gyms will reopen.&lt;/p&gt;

&lt;p&gt;So, on the evening of April 22nd, I submitted my app to App Store Connect. Only to be immediately rejected in the upload process because my app icons had an alpha channel in them.  It was bedtime, so I left it for the morning.&lt;/p&gt;

&lt;p&gt;The next morning I fixed my icon errors. I did this by opening the icons in the “Preview” app and then exporting them with the “Alpha” option unchecked.  A quick and easy fix. This time the app upload worked.  &lt;/p&gt;

&lt;p&gt;Looking through the TestFlight requirements I saw a privacy policy was needed.  I looked at some online policy generators. But these tools were for apps that used user data. My app doesn’t use third-party APIs and it stores everything locally to the device. So, I wrote a very simple privacy policy and uploaded it to my blog website.  It will have a more permanent home later, I just wanted to get the app up on TestFlight.  I submitted my app.&lt;/p&gt;

&lt;p&gt;The next morning, April 24th, I received an email from App Store Connect. My app was “not approved for beta testing”.  The reviewers wanted to see a video of my app in action. It had to be a video of the app running on an actual Apple Watch, I could not use the simulator.&lt;/p&gt;

&lt;p&gt;On the weekend I found some quiet time to video my app. The kids were quietly watching a cartoon, so I could record without being disturbed. It was a bit awkward videoing my wrist, but I got it done. I uploaded the video to YouTube as an unlisted video and sent the link.  &lt;/p&gt;

&lt;p&gt;I heard back from App Store Connect two days later, on Tuesday, April 28th. My app was approved for TestFlight! Hurray!&lt;/p&gt;

&lt;p&gt;My husband and I installed it on our watches. The app immediately crashed for both of us.  Cry!&lt;/p&gt;

&lt;p&gt;I spent the next two days figuring out why my app crashed when installed from TestFlight.  It worked fine when I installed it via XCode. It was a lot of learning about crash reports, looking at crash reports, and googling. I will write about what I learned in another blog post.&lt;/p&gt;

&lt;p&gt;I &lt;a href="https://stackoverflow.com/q/61529502/4704303"&gt;posted a question to StackOverflow&lt;/a&gt; about my issue.  This helped me gather my thoughts and clarify my issue.  I had some more ideas, which lead me to this question: &lt;a href="https://stackoverflow.com/questions/58801669/testflight-installed-app-crash-with-swift-package-manager-dependencies"&gt;ios - TestFlight installed app crash with Swift Package Manager dependencies - Stack Overflow&lt;/a&gt;. It solved my problem!  I had to set the ‘DEAD_CODE_STRIPPING = NO’ property in the building settings.&lt;/p&gt;

&lt;p&gt;It was such a good feeling to finally see my app working! I did not expect it to take a week to get my beta test working. But I am happy I solved my issues and got it launched.  &lt;/p&gt;

&lt;p&gt;I would appreciate feedback on my app! I’ve created a public test link: &lt;a href="https://testflight.apple.com/join/s0jqslOG"&gt;Join the Barbell Helper beta - TestFlight - Apple&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Thank you for reading!  Now onto the next milestone: launching my app on the App Store!&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>testing</category>
      <category>ios</category>
      <category>testflight</category>
    </item>
  </channel>
</rss>
