<?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: Suulola Oluwaseyi</title>
    <description>The latest articles on DEV Community by Suulola Oluwaseyi (@suulola).</description>
    <link>https://dev.to/suulola</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%2F306253%2F29476ca7-e494-4fbf-bdc6-c117cccd5aef.jpg</url>
      <title>DEV Community: Suulola Oluwaseyi</title>
      <link>https://dev.to/suulola</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/suulola"/>
    <language>en</language>
    <item>
      <title>Building a full-stack Typescript project with GraphQL and React Native - Part 1: Setup</title>
      <dc:creator>Suulola Oluwaseyi</dc:creator>
      <pubDate>Fri, 27 Jan 2023 09:33:35 +0000</pubDate>
      <link>https://dev.to/suulola/building-a-full-stack-typescript-project-with-graphql-and-react-native-part-1-setup-4dho</link>
      <guid>https://dev.to/suulola/building-a-full-stack-typescript-project-with-graphql-and-react-native-part-1-setup-4dho</guid>
      <description>&lt;p&gt;For this series, I will be building a &lt;code&gt;Fund raising app&lt;/code&gt;. This application will allow users to request for funds towards a cause and for other users who want to assist to donate a certain amount towards the cause.&lt;/p&gt;

&lt;p&gt;Some other use cases are&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Users are able to login and signup(Authentication)&lt;/li&gt;
&lt;li&gt;Users can create a cause for which they need donation towards and generate a link to share which others&lt;/li&gt;
&lt;li&gt;User who create a cause can read and delete the cause but not update unless you're a super admin(authorization)&lt;/li&gt;
&lt;li&gt;The link generated should take the invited users to the app if they have the app or direct them to the relevant store to download the mobile application(deep linking/dynamic linking)&lt;/li&gt;
&lt;li&gt;Users should be able to upvote and downvote a cause. Once a cause is downvoted up to 10 times, it will be reviewed by a moderator. If downvoted up to 100 times, it's marked as fraudulent and stops receiving funds.&lt;/li&gt;
&lt;li&gt;Still considering if I want to include a chat functionality that will include Socket implementation or not&lt;/li&gt;
&lt;li&gt;Other users should be able to make payment on the app. The payment will be handled using Paystack SDK.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's dive in&lt;/p&gt;

&lt;h2&gt;
  
  
  Backend Set Up
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; Create your project folder. I will be calling mine &lt;code&gt;fund-raising-backend&lt;/code&gt; and navigate into it&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;mkdir&lt;/span&gt; &lt;span class="nx"&gt;fund&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;raising&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;backend&lt;/span&gt;
&lt;span class="nx"&gt;cd&lt;/span&gt; &lt;span class="nx"&gt;fund&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;raising&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;backend&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2.&lt;/strong&gt; Initialize the package.json and open in your favorite code editor. I will be using VS Code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;init&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv5m6pky47jokrfb5zng6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv5m6pky47jokrfb5zng6.png" alt="Project Initialization" width="800" height="443"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.&lt;/strong&gt; Now, let's install the necessary dependencies&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add graphql @apollo/server @prisma/client 
yarn add -D typescript @types/node prisma
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some quick notes&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We will be using Prisma as our ORM to interface with our Postgres database. For creating a Postgres DB on Render, follow the &lt;a href="https://render.com/docs/databases#creating-a-database" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt;. At the end, you should have an external connection URL that looks like &lt;code&gt;postgres://USERNAME:PASSWORD@EXTERNAL_HOST_URL/DATABASE&lt;/code&gt;. Render offers a free database plan that lasts for 3 months&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4.&lt;/strong&gt; Set up Typescript configuration by creating the &lt;code&gt;tsconfig.json&lt;/code&gt; file and specifying the options while configuring the script 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="nb"&gt;touch &lt;/span&gt;tsconfig.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;"compilerOptions"&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;"rootDirs"&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="s2"&gt;"src"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"outDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lib"&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="s2"&gt;"es2020"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"es2020"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"esnext"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"moduleResolution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"esModuleInterop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"types"&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="s2"&gt;"node"&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;In our package.json, update the &lt;code&gt;scripts&lt;/code&gt; compile typescript while running the compiled file and ensure the &lt;code&gt;type&lt;/code&gt; is specified as &lt;code&gt;module&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"module"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&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;"compile"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tsc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm run compile &amp;amp;&amp;amp; node ./dist/index.js"&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="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, we've not created any file except for our package.json and the node_modules folder from our dependencies. &lt;/p&gt;

&lt;p&gt;The next step will be to &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Define a schema&lt;/li&gt;
&lt;li&gt;Define a resolver&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>gratitude</category>
    </item>
    <item>
      <title>CICD for React Native - Android and iOS</title>
      <dc:creator>Suulola Oluwaseyi</dc:creator>
      <pubDate>Wed, 11 Jan 2023 05:33:27 +0000</pubDate>
      <link>https://dev.to/suulola/ci-cd-for-android-and-ios-using-react-native-1dbm</link>
      <guid>https://dev.to/suulola/ci-cd-for-android-and-ios-using-react-native-1dbm</guid>
      <description>&lt;p&gt;When creating a React Native application, it soon becomes evident that generating an APK each time a change is made becomes really tedious. Coupled with the fact that you need to Archive the build on Xcode and Distribute to Appstore for either publishing on Testflight or Appstore.&lt;/p&gt;

&lt;p&gt;I used two different approaches for auto deployment on both platforms but both are trigger on pushing to a particular branch.&lt;/p&gt;

&lt;p&gt;While in development, I use the &lt;code&gt;dev&lt;/code&gt; branch as the active branch all changes are merged into&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For Android, I decided to go with &lt;code&gt;GitHub Actions&lt;/code&gt; that then pushes the app to &lt;code&gt;Firebase App Distribution&lt;/code&gt; and notification sent out to the testers via email&lt;/li&gt;
&lt;li&gt;For iOS, &lt;code&gt;XCode Cloud&lt;/code&gt; did the job for me which is published to Testflight automatically and notification sent out to the users via push notification and email&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's get right in&lt;/p&gt;

&lt;h2&gt;
  
  
  Android using GitHub Action and Firebase App Distribution
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Create a &lt;code&gt;.github/workflows&lt;/code&gt; folder in the root of the project&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;build.yml&lt;/code&gt; file inside the &lt;code&gt;.github/workflows&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp71ftd3tbf6xeptcrhgw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp71ftd3tbf6xeptcrhgw.png" alt="build.yml creation" width="762" height="148"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add the code below. Pretty self explanatory and the name explains each action
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name: Build And Deploy App To Firebase Distribution

on:
  push:
    branches:
      - dev

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: set up JDK 1.8
        uses: actions/setup-java@v3
        with:
          distribution: temurin
          java-version: 11
      - name: Setup Android SDK
        uses: android-actions/setup-android@v2
      - name: Setup Gradle
        uses: gradle/gradle-build-action@v2
      - name: Get yarn cache directory path
        id: yarn-cache-dir-path
        run: echo "::set-output name=dir::$(yarn cache dir)"
      - name: Restore node_modules from cache
        uses: actions/cache@v2
        id: yarn-cache
        with:
          path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
          key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
          restore-keys: |
            ${{ runner.os }}-yarn-
      - name: Install dependencies
        run: yarn install
      - name: Make Gradlew Executable
        run: cd android &amp;amp;&amp;amp; chmod +x ./gradlew
      - name: build release
        run: cd android &amp;amp;&amp;amp; ./gradlew clean &amp;amp;&amp;amp; ./gradlew assembleRelease --no-daemon
      - name: Upload artifact to Firebase App Distribution
        uses: wzieba/Firebase-Distribution-Github-Action@v1
        with:
          appId: ${{secrets.FIREBASE_APP_ID}}
          token: ${{secrets.FIREBASE_TOKEN}}
          groups: test-group
          file: android/app/build/outputs/apk/release/app-universal-release.apk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;You'll need to specify the FIREBASE_APP_ID and FIREBASE_TOKEN gotten from Firebase when setting up Firebase App distribution&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwjfo8yz0etetllz3nrqk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwjfo8yz0etetllz3nrqk.png" alt="GitHub Secret" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Once that is done and you push, the CICD should kick in. The progress and result can be accessed under the &lt;code&gt;Actions&lt;/code&gt; tab.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fibcvcd3f20umdukruiu0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fibcvcd3f20umdukruiu0.png" alt="GitHub Action" width="800" height="101"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Some debugging tips
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;try to run &lt;code&gt;cd android &amp;amp;&amp;amp; ./gradlew clean &amp;amp;&amp;amp; ./gradlew assembleRelease&lt;/code&gt; locally first and ensure it's successful&lt;/li&gt;
&lt;li&gt;The GitHub Action event will point out what is wrong if the build fails for you to debug and fix&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  iOS using XCode and Testflight
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Create a &lt;code&gt;.ci_scripts&lt;/code&gt; folder in &lt;code&gt;/ios&lt;/code&gt; folder of your project&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;ci_post_clone.sh&lt;/code&gt; file inside the &lt;code&gt;.github/workflows&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw5e7l48612ev0k8b389s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw5e7l48612ev0k8b389s.png" alt="iOS folder structure" width="712" height="144"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Paste this code in &lt;code&gt;ci_post_clone.sh&lt;/code&gt; file
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/sh

export HOMEBREW_NO_INSTALL_CLEANUP=TRUE
brew install cocoapods
# have to add node yourself
brew install node@16
# link it to the path
brew link node@16

brew install yarn

# Install dependencies you manage with CocoaPods.
yarn
pod install
# the sed command from RN cant find the file... so we have to run it ourselves
sed -i -e  $'s/ &amp;amp;&amp;amp; (__IPHONE_OS_VERSION_MIN_REQUIRED &amp;lt; __IPHONE_10_0)//' /Volumes/workspace/repository/ios/Pods/RCT-Folly/folly/portability/Time.h
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;OPTIONAL&lt;/strong&gt;: Add this to your &lt;code&gt;Info.plist&lt;/code&gt; file to resolve the encryption dialog check required before publishing an app to Testflight&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;key&amp;gt;ITSAppUsesNonExemptEncryption&amp;lt;/key&amp;gt;
&amp;lt;false/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Push to the github branch you want to use for deployment. In my case &lt;code&gt;dev&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Launch Xcode and go to &lt;code&gt;Product&lt;/code&gt; and Select &lt;code&gt;Xcode Cloud&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Follow the prompt and connect Xcode cloud to your GitHub project, specifying the &lt;code&gt;dev&lt;/code&gt; branch or whatever branch you are working on&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The remaining work will be done on the Appstore connect interface. Will update this article with those additional steps later on&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let me know if you have any questions.&lt;/p&gt;

&lt;p&gt;Voila &lt;/p&gt;

</description>
      <category>welcome</category>
      <category>learning</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Error Fix: Source "@openzeppelin/contracts/token/ERC721/ERC721.sol" not found: File import callback not supported</title>
      <dc:creator>Suulola Oluwaseyi</dc:creator>
      <pubDate>Thu, 31 Mar 2022 08:55:32 +0000</pubDate>
      <link>https://dev.to/suulola/error-fix-source-openzeppelincontractstokenerc721erc721sol-not-found-file-import-callback-not-supported-5ccb</link>
      <guid>https://dev.to/suulola/error-fix-source-openzeppelincontractstokenerc721erc721sol-not-found-file-import-callback-not-supported-5ccb</guid>
      <description>&lt;p&gt;If you've tried to work with @openzeppelin/contracts on VS Code, you might come across this error &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bs3tg4PG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8najv54hnvhu89pocw16.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bs3tg4PG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8najv54hnvhu89pocw16.png" alt="VS Code Error" width="880" height="178"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The issue above is not with your code but with a particular VS Code extension - &lt;em&gt;Solidity extension&lt;/em&gt; by Juan Blanco.&lt;/p&gt;

&lt;p&gt;The latest version of v0.0.137 shows that error. To correct this&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to Extensions and search for Solidity&lt;/li&gt;
&lt;li&gt;You should see something like this&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bChHNHAR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7qfpdq5srrgwtnvb9h2c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bChHNHAR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7qfpdq5srrgwtnvb9h2c.png" alt="Juan Blanco Solidity VS Code extension" width="880" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click on the dropdown beside the Uninstall button and select &lt;em&gt;Install Another Version&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0eOEQ0_K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dvxw71bd0ohmsd0hfvjp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0eOEQ0_K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dvxw71bd0ohmsd0hfvjp.png" alt="Install Another Version " width="880" height="368"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Select Version 0.0.135 from the list of options&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--x-d99xwv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x4xzuspbv168jyy5tckn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--x-d99xwv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x4xzuspbv168jyy5tckn.png" alt="Version List" width="880" height="453"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Allow the version to install&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GeRBUA8M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/av55nbe80ocnjygcrvff.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GeRBUA8M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/av55nbe80ocnjygcrvff.png" alt="Installation" width="813" height="289"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Once the installation is done, you will be required to reload VS Code and the error should be gone at this point&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--prQy0RUQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/968i0y98l0kgp9eoy5ld.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--prQy0RUQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/968i0y98l0kgp9eoy5ld.png" alt="Restart VS Code" width="880" height="224"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Viola🥳🥳🥳🥳🥳🥳&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IyH3oa4k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7tcf23uyc42pyv1sgohx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IyH3oa4k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7tcf23uyc42pyv1sgohx.png" alt="Error Fixed" width="848" height="175"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The issue should be gone by now.&lt;/p&gt;

&lt;p&gt;Hopefully, this issue is fixed in the next version release of v0.0.138&lt;/p&gt;

</description>
      <category>openzeppelin</category>
      <category>web3</category>
      <category>solidity</category>
      <category>vscode</category>
    </item>
    <item>
      <title>Change to Test Network on Metamask</title>
      <dc:creator>Suulola Oluwaseyi</dc:creator>
      <pubDate>Sun, 20 Mar 2022 09:21:23 +0000</pubDate>
      <link>https://dev.to/suulola/change-to-test-network-on-metamask-pbh</link>
      <guid>https://dev.to/suulola/change-to-test-network-on-metamask-pbh</guid>
      <description>&lt;p&gt;I recently started learning web3 and had to use Metamask. In recent updates of MetaMask, only the Ethereum Mainnet shows by default&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--O4oOjZjo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jze6c8y2n6v1826bnj2i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--O4oOjZjo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jze6c8y2n6v1826bnj2i.png" alt="Default Metamask display" width="752" height="496"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In development, I need to change to a Test Network not work with the Ethereum Mainnet - until the application gets to production.&lt;/p&gt;

&lt;p&gt;Clicking on Add Network shows you the list of all the test network but clicking on it doesn't change the network you are on. You can only Add an external network here - not one of the predefined ones listed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9-B2zLH0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7m333xuxznb72agclksj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9-B2zLH0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7m333xuxznb72agclksj.png" alt="Network Page" width="880" height="665"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;h2&gt;
  
  
  So how do i change my network
&lt;/h2&gt;
&lt;/blockquote&gt;

&lt;p&gt;Simple, &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to Settings &amp;gt; Advanced&lt;/li&gt;
&lt;li&gt;Look for &lt;strong&gt;Show test networks&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2DncU2vN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/utopmgf1tyxlbp5a91ev.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2DncU2vN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/utopmgf1tyxlbp5a91ev.png" alt="Advanced Settings Preview" width="880" height="705"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Toggle it ON
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BdNlBfY5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3nzhn120skfva97wm509.png" alt="Enabled Show Test Networks" width="848" height="286"&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Viola🥳🥳🥳🥳 Problem Solved. Now you can switch between the Ethereum Mainnet and the Test Network from the dropdown above.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VcR1w1JR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6aivwyu5zo18ib1h5fw7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VcR1w1JR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6aivwyu5zo18ib1h5fw7.png" alt="Updated Metamask dropdown display" width="848" height="964"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Simple but can be tricky.&lt;/p&gt;

&lt;p&gt;I hope this helps.&lt;/p&gt;

</description>
      <category>metamask</category>
      <category>blockchain</category>
      <category>solidity</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Error encountered while setting up React Native using Apple's M1 chip system</title>
      <dc:creator>Suulola Oluwaseyi</dc:creator>
      <pubDate>Thu, 23 Dec 2021 22:43:43 +0000</pubDate>
      <link>https://dev.to/suulola/error-encountered-while-setting-up-react-native-using-apples-m1-chip-system-1gg5</link>
      <guid>https://dev.to/suulola/error-encountered-while-setting-up-react-native-using-apples-m1-chip-system-1gg5</guid>
      <description>&lt;p&gt;First of all, having read a lot of information online - i knew it was not gonna be an easy ride running my existing project on Apple's M1 chip&lt;/p&gt;

&lt;p&gt;The first thing I did was to create an empty project after following the React Native environment setup documentation on &lt;a href="https://reactnative.dev/docs/environment-setup"&gt;https://reactnative.dev/docs/environment-setup&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thereafter, I ran &lt;/p&gt;

&lt;p&gt;npx react-native init AwesomeTSProject --template react-native-template-typescript&lt;/p&gt;

&lt;p&gt;to create a broilerplate project. The initailization was successful until I ran npm run ios&lt;/p&gt;

&lt;p&gt;1st Error: Failed to build iOS project. We ran "xcodebuild" command but it exited with error code 65.&lt;/p&gt;

&lt;p&gt;Solved that by following this Stackoverflow post at &lt;a href="https://stackoverflow.com/questions/65458086/failed-to-build-ios-project-we-ran-xcodebuild-command-but-it-exited-with-erro"&gt;https://stackoverflow.com/questions/65458086/failed-to-build-ios-project-we-ran-xcodebuild-command-but-it-exited-with-erro&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this stage, my project launched successfully.&lt;/p&gt;

&lt;p&gt;Funny enough, I have answered a Stackoverflow question before relating to this at &lt;a href="https://stackoverflow.com/questions/66062663/react-native-npx-react-native-run-ios-doesnt-work-after-initializing-the-proj/66143494#66143494"&gt;https://stackoverflow.com/questions/66062663/react-native-npx-react-native-run-ios-doesnt-work-after-initializing-the-proj/66143494#66143494&lt;/a&gt; but yet my solution didnt come to me at the moment i had the issue.&lt;/p&gt;

&lt;p&gt;After applying the fix, I was able to run my app successfully.&lt;/p&gt;

&lt;p&gt;The second stage involves bringing in my existing codebas.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Using Netify Redirect</title>
      <dc:creator>Suulola Oluwaseyi</dc:creator>
      <pubDate>Sun, 04 Apr 2021 12:07:58 +0000</pubDate>
      <link>https://dev.to/suulola/using-netify-redirect-4k62</link>
      <guid>https://dev.to/suulola/using-netify-redirect-4k62</guid>
      <description>&lt;p&gt;Recently, I had two annoying errors which I had to deal with on Netlify and the "_redirects" file solved both for me.&lt;/p&gt;

&lt;p&gt;The first issue I encountered was&lt;/p&gt;

&lt;h4&gt;
  
  
  CORS
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--i0nWYrRV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9m2cph9c5a7lvw2r82bt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i0nWYrRV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9m2cph9c5a7lvw2r82bt.png" alt="CORS"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Access to fetch at blocked by cors policy no 'access-control-allow-origin' header is present&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The Almighty CORS issues - The annoying thing about CORS for me is the fact that I test all the APIs on Postman, tell the backend developer it's working - only for me to start the integration and viola - nothing works because of CORS. &lt;/p&gt;

&lt;p&gt;The normal approach will be to tell the backend developer to make the necessary adjustment - but in my case, this was a public API I have no control over.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;My first solution was to use &lt;strong&gt;&lt;a href="https://cors-anywhere.herokuapp.com/corsdemo"&gt;https://cors-anywhere.herokuapp.com/corsdemo&lt;/a&gt;&lt;/strong&gt;. Works fine in production but highly restricted on production.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The second solution that came to mind was to write a quick API - call the endpoint in my API which then serves my front-end. That's a longer but secure route which I unfortunately didn't implement.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The third solution was using &lt;strong&gt;redirects&lt;/strong&gt; which is what I used eventually&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Here are the steps using React
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Create a &lt;em&gt;_redirects&lt;/em&gt; file in your &lt;em&gt;public&lt;/em&gt; directory. i.e your_project_folder/public/_redirects&lt;/li&gt;
&lt;li&gt;Using the format below, redirect to the endpoint
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/api/fetchCorsEndpointData https://corsendpoint.com/data 200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now anytime you want to fetch data from &lt;a href="https://corsendpoint.com/data"&gt;https://corsendpoint.com/data&lt;/a&gt;, make an api call to &lt;a href="https://yoursubdomain.netlify.app/api/fetchCorsEndpointData"&gt;https://yoursubdomain.netlify.app/api/fetchCorsEndpointData&lt;/a&gt; and viola - you get your data.&lt;/p&gt;

&lt;p&gt;The second issue was &lt;/p&gt;

&lt;h4&gt;
  
  
  Mixed Content
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EbLNQas7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5p5i1ow68z4qxustc3s1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EbLNQas7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5p5i1ow68z4qxustc3s1.png" alt="Mixed Content: The page at was loaded over HTTPS, but requested an insecure resource ''. This request has been blocked; the content must be served over HTTPS."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Mixed Content: The page at was loaded over HTTPS, but requested an insecure resource ''. This request has been blocked; the content must be served over HTTPS.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The public API I was accessing was deployed on HTTP and not HTTPS - using the method specified above, I was able to access resources on the endpoint on Netlify. Specify in your &lt;code&gt;_redirects&lt;/code&gt; file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/api/unsecureRoute http://unsecureendpoint.com/data 200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In your codebase, simply call &lt;code&gt;https://yoursubdomain.netlify.app/api/unsecureRoute&lt;/code&gt; and it will fetch the resource on &lt;code&gt;http://unsecureendpoint.com/data&lt;/code&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  BONUS
&lt;/h1&gt;

&lt;p&gt;The third use case for me is&lt;/p&gt;

&lt;h4&gt;
  
  
  Page Refresh
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_L3N5ZT7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m9pwq8mwte18dbbochcd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_L3N5ZT7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m9pwq8mwte18dbbochcd.png" alt="Page Not Found"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Looks like you've followed a broken link or entered a url that doesn't exist on this site.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I get "Page Not Found" on refresh of a page on Netlify - the fix for me was to add the snippet below as the last line in my &lt;code&gt;_redirects&lt;/code&gt; file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* /index.html 200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I hope this helped fix whatever issue you have when deploying your React App on Netlify - let me know your thought in the comment section.&lt;/p&gt;

&lt;p&gt;Viola✨✨✨✨&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Building with Vue 3 with tailwindcss</title>
      <dc:creator>Suulola Oluwaseyi</dc:creator>
      <pubDate>Sat, 26 Dec 2020 19:17:08 +0000</pubDate>
      <link>https://dev.to/suulola/building-with-vue-3-with-tailwindcss-29cf</link>
      <guid>https://dev.to/suulola/building-with-vue-3-with-tailwindcss-29cf</guid>
      <description>&lt;p&gt;This tutorial will be building a full project using Vue 3 and combining it with the magic of Tailwindcss to produce a responsive multipage website &lt;/p&gt;

&lt;p&gt;Below is the landing page of the webpage that will be built&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Tzh51hmu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/7gxqlvhg42q0urm033nn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Tzh51hmu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/7gxqlvhg42q0urm033nn.png" alt="Vue 3 Webpage"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some of the concepts that will be learnt in the process include&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setting up a Vue project with tailwindcss&lt;/li&gt;
&lt;li&gt;State management with vuex &lt;/li&gt;
&lt;li&gt;Routing, Composition API in Vue 3&lt;/li&gt;
&lt;li&gt;Creating reusable components&lt;/li&gt;
&lt;li&gt;Persisting state in Vue application&lt;/li&gt;
&lt;li&gt;Interacting with APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It promises to be an interesting an exciting journey.&lt;/p&gt;

&lt;p&gt;The codebase can be accessed on &lt;a href="https://github.com/mySuulola/learning-vue"&gt;GitHub Here&lt;/a&gt;. Give it a star if you find it helpful and your pull requests are definitely welcomed.&lt;/p&gt;

&lt;p&gt;The choice of tailwind is become of the flexibility the utility library gives - especially for responsiveness and dark/light mode which is something we will implement across the application.&lt;/p&gt;

&lt;p&gt;You can check the final product &lt;a href="https://vue3x.netlify.app/"&gt;here as hosted on Netlify&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Drop your comment in the comment box and I hope you are as excited as I am to embark on this quest.&lt;/p&gt;

&lt;p&gt;Your questions and comments are welcomed in the comment section.&lt;/p&gt;

&lt;p&gt;Cheers&lt;/p&gt;

</description>
      <category>vue3</category>
      <category>vuex</category>
      <category>router</category>
      <category>tailwindcss</category>
    </item>
    <item>
      <title>Hello Testing with Cypress and Selenium</title>
      <dc:creator>Suulola Oluwaseyi</dc:creator>
      <pubDate>Thu, 02 Jan 2020 14:08:56 +0000</pubDate>
      <link>https://dev.to/suulola/hello-testing-with-cypress-and-selenium-2ejc</link>
      <guid>https://dev.to/suulola/hello-testing-with-cypress-and-selenium-2ejc</guid>
      <description>&lt;p&gt;&lt;i&gt;&lt;br&gt;
This article will test a simple log in page. Consider it the Hello World of testing using  two frameworks.&lt;br&gt;
 I'll be writing using two different languages.&lt;/i&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Selenium using Java&lt;/li&gt;
&lt;li&gt;Cypress using JavaScript&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;For the sake of brevity, the article will be divided into three sections&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Setting up and Testing with Cypress&lt;/li&gt;
&lt;li&gt;Testing with Selenium&lt;/li&gt;
&lt;li&gt;Comparison between Cypress and Selenium and use case scenarios&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;Setting up and Cypress installation&lt;/h4&gt;

&lt;p&gt;
Let’s call the web app we will work on NASS Protector Launch. Anytime there is an alien attack on the planet, we can always login into the web app and click the “Rescue” button to wade off the incoming attacks. As such we need to monitor our login page to ensure it never malfuncions – and even if it does, for us to know on time

 &lt;/p&gt;

&lt;p&gt;
Since is a grand app,our engineers need to keep working round the clock to add additional more functionality to save the world but our concern here is to ensure the login always works regardless of the changes being made and for us to detect as fast as possible if any major functionality is broken
And this is where automation test comes in
&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Let's setup the page first before we embark on automating testing&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&lt;i&gt;Here is a simple codebase for the login page and the dashboard with plain html, a little css and javascript.&lt;/i&gt;&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Now that our login and dashboard pages are setup. It is time to move on to Cypress Installation&lt;/p&gt;

&lt;p&gt;We start by creating a package.json  to manage the cypress dependency that will be installed later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm init
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;I prefer using npm init -y then make any edit from there. Its faster that way. The next thing is to install cypresss. I will be using npm&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install cypress –only=dev
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;If you prefer yarn&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt; yarn add cypress
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;When its done, you will get something like this&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--M1ED4Dq3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/xy69zvr50fqcwqdulvft.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--M1ED4Dq3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/xy69zvr50fqcwqdulvft.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this stage, your project structure should resemble this&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qilqa6Cc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/98cp0bwg3bmmwbdprvwl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qilqa6Cc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/98cp0bwg3bmmwbdprvwl.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;to run cypress, simply run&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx cypress open

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


&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pjCcgU8a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/qhpc94kpdqrm78vjm604.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pjCcgU8a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/qhpc94kpdqrm78vjm604.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
This pops up the above test  modal suite you will get very familiar with very soon and if you look closely a folder will be created in your project directory named cypress. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KmKnyIQu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/et2xb0n7s83b92bceiy6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KmKnyIQu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/et2xb0n7s83b92bceiy6.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you got here, you’ve successfully set up cypress and you’re good to start writing code using Cypress. You will be writing most of your test code inside the cypress/integration folder. There are some examples in the example folder to familiarize yourself with Cypress syntax.&lt;/p&gt;

&lt;p&gt;I normally delete the example folder but its a good place to start learn the workings of Cypress.&lt;/p&gt;

&lt;p&gt;The first thing is to create a file within the integration folder, I will call it protector_launch and it will contain all the tests that has to do with this particular application while deleting the example folder.&lt;/p&gt;

&lt;p&gt;Inside the protector_launch,  create a test script. I will call mine login.spec.js&lt;/p&gt;

&lt;p&gt;the ‘spec’ in-between is to show this is a test file and not a typical JavaScript file.&lt;/p&gt;

&lt;p&gt;We start with a basic outline&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;the first line which is the reference line indicates that this is a cypress file and this aids in bringing auto-suggestion of cypress syntax while typing. Without it, you wont have auto-suggest.&lt;/p&gt;

&lt;p&gt;If you’ve ever worked with mocha/chai or similar testing frameworks, you will be familiar with the &lt;i&gt;describe&lt;/i&gt; and &lt;i&gt;it&lt;/i&gt; syntax used in cypress.&lt;/p&gt;

&lt;p&gt;The describe block is a way to group our test.It takes two argument&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; a string name describing what the test group is about&lt;/li&gt;
&lt;li&gt;a callback function where we write each of our &lt;i&gt;it&lt;/i&gt; statements that execute the test &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Inside the describe block callback, let's start writing our test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  before(() =&amp;gt; {
    cy.visit("./../../../index.html")
  })
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The first thing we want to do before we run our test is to visit the url. In this case, it is a relative path on the local machine. Assuming, i want to test my facebook login url&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cy.visit("https://web.facebook.com/login")
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Now, this is where the fun begins. We are set to write our first test here after visiting the web page.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  it("checks that the input fields are visible", () =&amp;gt; {
    cy.get('#mail').should('be.visible')
    cy.get('#pass').should('be.visible')
  })
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The &lt;i&gt;it&lt;/i&gt; block also takes the same arguments as the describe block except that the content of the callback are usually actual tests.&lt;br&gt;
Here we use  cy.get() to locate the email input field that has an id of mail. You can use your developer tools to check the selector of the element you want to target. cy.get() in cypress acts like document.querySelector() in javascript. Just specify the selector and it does the magic.&lt;/p&gt;

&lt;p&gt;After selecting the elements with id of mail and pass. We use the should method to check if it is visible on the page, if it is visible then the test will pass else the test fails.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  it("a user cannot login with incorrect credentials", () =&amp;gt; {
    cy.get('#mail').type('user@alien.com')
    cy.get('#pass').type('frompluto')
    cy.contains('Log').click()
    cy.url().should('include', '/index.html')
    cy.get('#mail').should('be.visible')
    cy.get('p').should('not.have.text', 'Now you can save the world')
  })

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


&lt;p&gt;The next test is to check if a user can login in with incorrect credentials. &lt;i&gt;type&lt;/i&gt; is used to type the details in the input field selected. &lt;br&gt;
cy.contains() searches for an element on the page that contains the text, then .click() clicks on that element.&lt;/p&gt;

&lt;p&gt;The assertion here tests that the user is still on the same page with&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cy.url().should('include', '/index.html')
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;A successful login will have changed the page to dashboard.html so if that passes, then the login failed. The other assertions ensures the input field with an id of mail is still present on the page and that the page does not contain the text present on the dashboard.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  it("logins into the application with the correct credentials", () =&amp;gt; {
    cy.get('#mail').type('test@test.com')
    cy.get('#pass').type('tester')
    cy.contains('Log').click()
    cy.url().should('include', '/dashboard.html')
    cy.get('p').should('have.text', 'Now you can save the world')
  })
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This next test code does the same thing as above except that it checks if a user with the correct credentials is able to login to the application and move to the dashboard where you can save the world.&lt;/p&gt;

&lt;p&gt;Apart from web page testing, cypress can also be used for&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API testing&lt;/li&gt;
&lt;li&gt;Performance/Load testing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using the current application, we can write a simple GET request&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  // a simple api request mock
  it("checks if the app is up and running when called", () =&amp;gt; {

    // the request body contains the method(whether GET, POST, PUT, DELETE), the endpoint/url and the necessary header options
    cy.request({
      method: 'GET',
      url: './../../../index.html',
      headers: {
        accept: 'text/html',
      }

    })
    .then(response =&amp;gt; {
      // this is where the response gotten from the server is tested
      // 200 response means its okay and good
      // when not sure, you can always console.log(response)
      expect(response.status).to.eq(200)
      expect(response.statusText).to.eq("OK")
      expect(response).to.have.property("headers");
      expect(response.body).to.have.length.greaterThan(500);

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


&lt;p&gt;A simple performance test with the current web page will look like this&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  it('check page load time', () =&amp;gt; {
    cy.visit('./../../../index.html', {
      onBeforeLoad: win =&amp;gt; {
        // marks the start time when the request is launched
        win.performance.mark("start-loading")
      },
      onLoad: win =&amp;gt; {
        // marks the time when the page is displayed
        win.performance.mark("end-loading")
      }
    }).its("performance").then(perf =&amp;gt; {
      // when not sure, you can always console.log("this is perf",perf)

      // this is where the measure takes place. it subtracts the end-loading from the start-loading and stores it in pageLoad
      perf.measure("pageLoad", "start-loading", "end-loading")
      const load = perf.getEntriesByName("pageLoad")[0]
      // checks if the load duration is more than 10ms
      assert.isAtMost(load.duration, 10)
    })


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


&lt;p&gt;I added comments to the code to make it explanatory enough to understand. &lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;The full codebase can be found on&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qF2jUiUG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo-6a5bca60a4ebf959a6df7f08217acd07ac2bc285164fae041eacb8a148b1bab9.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/mySuulola"&gt;
        mySuulola
      &lt;/a&gt; / &lt;a href="https://github.com/mySuulola/hello_test"&gt;
        hello_test
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
hello_test&lt;/h1&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/mySuulola/hello_test"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;Comments, Questions and Observations are welcomed.&lt;/p&gt;

&lt;p&gt;Next is writing our Selenium test&lt;/p&gt;

</description>
      <category>selenium</category>
      <category>cypress</category>
      <category>testing</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
