<?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: Wade Zimmerman</title>
    <description>The latest articles on DEV Community by Wade Zimmerman (@wadecodez).</description>
    <link>https://dev.to/wadecodez</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%2F644317%2F7638381b-b402-45e4-8901-58be8f287a80.jpg</url>
      <title>DEV Community: Wade Zimmerman</title>
      <link>https://dev.to/wadecodez</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wadecodez"/>
    <language>en</language>
    <item>
      <title>Happy? Halloween? 🤔</title>
      <dc:creator>Wade Zimmerman</dc:creator>
      <pubDate>Thu, 31 Oct 2024 02:01:56 +0000</pubDate>
      <link>https://dev.to/wadecodez/happy-halloween-4l8d</link>
      <guid>https://dev.to/wadecodez/happy-halloween-4l8d</guid>
      <description>&lt;p&gt;Acknowledging: &lt;a href="https://dev.to/best_codes/happy-halloween-klm"&gt;https://dev.to/best_codes/happy-halloween-klm&lt;/a&gt;&lt;br&gt;
Proof: &lt;a href="https://chatgpt.com/share/67238cc0-67ec-8001-a8c3-e8624bdaf41f" rel="noopener noreferrer"&gt;https://chatgpt.com/share/67238cc0-67ec-8001-a8c3-e8624bdaf41f&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Halloween in some parts of the world (usually rural) is about running around trying to spook people and allowing children to be mischievous. The trauma creates strong friendships. Religious groups tend to do there own thing.&lt;/p&gt;

&lt;p&gt;On the other hand, cities and urban areas tend to have policies in place to prevent chaos. Historically there have been some Halloween events that got out of hand. So those communities will celebrate a day early.&lt;/p&gt;

&lt;p&gt;It is also not uncommon for other cultures to celebrate Día de (los) Muertos during the first part of November.&lt;/p&gt;

&lt;p&gt;If you live in an area that has a mixture of all these cultures, "Halloween" could last for almost a week, which goes against some religions.&lt;/p&gt;

&lt;p&gt;If you want to critique AI, I made sure to post the artwork on &lt;a href="https://www.thetechgame.com/Forums/t=7838069/made-a-quick-banner.html" rel="noopener noreferrer"&gt;TTG&lt;/a&gt;, in case my original post was removed. Historically, TTG was used as an active forum for tech discussions before Reddit became popular. Now the site is mostly low quality content.&lt;/p&gt;

&lt;p&gt;My theory is, small divides in culture are amplified by AI, but LLMs are great for quickly picking up facts during a heated debate.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>halloween</category>
      <category>hacktoberfest</category>
    </item>
    <item>
      <title>WYD - Dating App for Gamers</title>
      <dc:creator>Wade Zimmerman</dc:creator>
      <pubDate>Sun, 27 Oct 2024 13:39:35 +0000</pubDate>
      <link>https://dev.to/wadecodez/wyd-dating-app-for-gamers-2kdl</link>
      <guid>https://dev.to/wadecodez/wyd-dating-app-for-gamers-2kdl</guid>
      <description>&lt;p&gt;&lt;a href="https://WYD.gg" rel="noopener noreferrer"&gt;https://WYD.gg&lt;/a&gt; is a dating platform for gamers that does not exist. If such a platform were to exist what features would it have? &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>mastodon</category>
      <category>beginners</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Thoughts on AI</title>
      <dc:creator>Wade Zimmerman</dc:creator>
      <pubDate>Wed, 23 Oct 2024 17:50:10 +0000</pubDate>
      <link>https://dev.to/wadecodez/thoughts-on-ai-2cho</link>
      <guid>https://dev.to/wadecodez/thoughts-on-ai-2cho</guid>
      <description>&lt;p&gt;The last few months I was not using X (formerly Twitter) as much if at all. When I opened the app the first thing I see is something lame about WordPress. My whole timeline is chaos and destruction so I figured I'd throw out the word "unprecedented" and see what happens.&lt;/p&gt;

&lt;p&gt;About a week later I check back to see if anyone replied to my question, and turns out I was 100% wrong. People actually replied! &lt;/p&gt;

&lt;p&gt;As someone who does not write WordPress, I had no idea what was being said. So naturally I asked if I should be concerned. Someone replied pretty quick even though it was nearly a week later.&lt;/p&gt;

&lt;p&gt;Apparently there was some kind of separation happening. &lt;/p&gt;

&lt;p&gt;That is when I started questioning the meaning. How could WordPress separate itself from PHP if it is written in PHP? Clearly I was living under a rock. Whatever I was reading online was not lining up with what developers were saying.&lt;/p&gt;

&lt;p&gt;As someone who also writes JavaScript and Rust from time to time, I know that software releases and updates can stir up a lot of drama. The last thing I would want to do is blindly start using PHP in the middle of drama. So I figured I would ask if switching back to PHP was the right move.&lt;/p&gt;

&lt;p&gt;Within the day I get another off the wall response. Okay. This is starting to make me curious. What news am I not caught up on? Before I could form a conclusion on my own, now, another comment shows up on the notification tab.&lt;/p&gt;

&lt;p&gt;Apparently PHP is not just separated but "Completely isolated from modern PHP tools and frameworks like Composer," and the security expert thinks an implosion would not affect much.&lt;/p&gt;

&lt;p&gt;That is relieving! Normally I use Laravel for my PHP applications that depend on composer. Which is also why I will try switching to other languages like C, Rust, or JavaScript every once in a while. Writing everything in Laravel can get boring.&lt;/p&gt;

&lt;p&gt;Over the last few years, Rust caught my eye as a blazingly fast language so I thought it was the best option.&lt;/p&gt;

&lt;p&gt;In the past, I was able to write enough Rust code to compile an iPhone app which was pretty neat! Was switching to Rust the right option for me? No probably not.&lt;/p&gt;

&lt;p&gt;So far, I kind of enjoy the language, but I think I'm going to try TypeScript first before getting too deep into Rust code.&lt;/p&gt;

&lt;p&gt;After all, TypeScript is a logical jump up from PHP in most cases. When your blog needs a little extra boost you could just write everything in TypeScript from the start. &lt;/p&gt;

&lt;p&gt;However, I do no fully enjoy TypeScript because there are so many packages to choose from. It seems like I have to relearn a new library or package for every project. It would be cool if TypeScript had some cool features like std::result.&lt;/p&gt;

&lt;p&gt;Thankfully that is where models come in handy. If I am uncertain, I can have a dialog about something tiny like Bootstrap, Tailwind, PHP, or JavaScript.&lt;/p&gt;

&lt;p&gt;It is great because I can jump right into a comment section and post something! &lt;/p&gt;

&lt;p&gt;How do you like to use ChatGPT to write comments or improve your writing skills?&lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>Artificial Intelligence and Coding</title>
      <dc:creator>Wade Zimmerman</dc:creator>
      <pubDate>Mon, 21 Oct 2024 15:17:06 +0000</pubDate>
      <link>https://dev.to/wadecodez/common-attack-vectors-3pcf</link>
      <guid>https://dev.to/wadecodez/common-attack-vectors-3pcf</guid>
      <description>&lt;p&gt;Today someone DM'd me about something called "Online code generation platform" which is a term I never heard of. What I found was more than a dozen options!&lt;/p&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%2Fcv6ui3i9gfkuazgsbhrm.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%2Fcv6ui3i9gfkuazgsbhrm.png" alt="Image description" width="800" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Part of me thinks this is cool and I want and jump right in, but anyone who has written software based on multiple NPM packages knows that having a bunch of options is not always a good thing. Sometimes there are too many options.&lt;/p&gt;

&lt;p&gt;For large businesses and cooperations I imagine navigating this is a breeze with security experts in place, but what does this mean for small businesses and content creators? &lt;/p&gt;

&lt;p&gt;How much future technology is too much? Do I need to keep up with all the videos? What are "TODO apps" of the future going to look like when Artificial Intelligence can do most the work?&lt;/p&gt;

&lt;p&gt;It is October and these are spooky questions to think about. Normally I would ask ChatGPT or Google for advice but sometimes I feel like the answers I get are not the same as hearing from the community. &lt;/p&gt;

&lt;p&gt;What do you think about Artificial Intelligence so far? &lt;/p&gt;

</description>
      <category>security</category>
      <category>discuss</category>
      <category>halloween</category>
    </item>
    <item>
      <title>Why Do Web Developers Suck at Making Video Games?</title>
      <dc:creator>Wade Zimmerman</dc:creator>
      <pubDate>Thu, 17 Oct 2024 16:27:00 +0000</pubDate>
      <link>https://dev.to/wadecodez/why-do-web-developers-suck-at-making-video-games-ki9</link>
      <guid>https://dev.to/wadecodez/why-do-web-developers-suck-at-making-video-games-ki9</guid>
      <description>&lt;p&gt;Optimization. &lt;/p&gt;

&lt;p&gt;Web browsers are highly optimized pieces of software that allow users to view 2D content on the web. This usually includes blog posts, videos, images, etc.&lt;/p&gt;

&lt;p&gt;In most web browsers, developers can display various forms of media using a series of stylish divs, lists, and artwork.&lt;/p&gt;

&lt;p&gt;Eventually, the developer begins to wonder. Why stop at rectangles? Why not use different shapes or effects? What are SVGs? Why not make it pop with 3D?&lt;/p&gt;

&lt;p&gt;If you have attempted to make a video game before, you know how difficult it is, but what makes working in the third dimension so difficult?&lt;/p&gt;

&lt;p&gt;Well, web browsers optimize for web pages. If you want to make a game in the browser, you need to use the HTML5 canvas element or leap to a game engine like Unity or Unreal.&lt;/p&gt;

&lt;p&gt;A game engine is an extensive library or web framework built for making games. Game engines make drawing sprites, playing audio, and creating cool visual effects more accessible. If I had an affiliate link, I'd link it here.&lt;/p&gt;

&lt;p&gt;For example, when you use React or TypeScript, sometimes you have to change the z-index, but the browser is orthographic, so you are effectively changing which layer an element is on. Getting that 3D perspective to work without a canvas is tedious. &lt;/p&gt;

&lt;p&gt;Yet, a developer could transform, scale, and warp 2D elements to provide a 3D look and feel, but then the DOM would constrain them, which could be better for SEO.&lt;/p&gt;

&lt;p&gt;There is something about that extra dimension that adds a lot of complexity. &lt;/p&gt;

&lt;p&gt;For example, did you know some game engines have different "up" directions? The Y and Z axes can point up, but both could quickly point down. &lt;/p&gt;

&lt;p&gt;That's one reason perspective is essential. A game engine enables developers to quickly change a camera's perspective. &lt;/p&gt;

&lt;p&gt;If you were to do the same thing in a 2D world, only three sides of an object would be visible at once. You would need a camera swivel to see the other side of an object. Think about how you would visualize the z-index from the side.&lt;/p&gt;

&lt;p&gt;They realize that different perspectives may limit one's ability to think of planes in a 3D space. On paper, there is a one-point, two-point, and three-point perspective. Are any of those correct?&lt;/p&gt;

&lt;p&gt;Art is where things get tricky. People define 3D differently.&lt;/p&gt;

&lt;p&gt;3D could mean popping off the screen, making an FPS game, or simply doing a barrel roll. &lt;/p&gt;

&lt;p&gt;In short, drawing a vanishing point looks weird. Instead, engines provide small node-like components so developers can add a camera object to a scene.&lt;/p&gt;

&lt;p&gt;The camera is responsible for translating 2D to 3D. If you want to get really good at 3D, having some knowledge of photography or film can go a long way.&lt;/p&gt;

&lt;p&gt;Unfortunately, my knowledge falls off. When I take a picture on my phone, I know that light reflects a certain way, like it does with your eyes. Otherwise, I'm clueless.&lt;/p&gt;

&lt;p&gt;Thankfully, through the advancement of technology, it is possible to project vertices onto a screen. &lt;/p&gt;

</description>
    </item>
    <item>
      <title>Skip the Dashboard</title>
      <dc:creator>Wade Zimmerman</dc:creator>
      <pubDate>Tue, 27 Aug 2024 22:17:49 +0000</pubDate>
      <link>https://dev.to/wadecodez/skip-the-dashboard-17l1</link>
      <guid>https://dev.to/wadecodez/skip-the-dashboard-17l1</guid>
      <description>&lt;p&gt;The era of data vomiting robots is plateauing. &lt;/p&gt;

&lt;p&gt;Typing like an MC is exhausting.&lt;/p&gt;

&lt;p&gt;There are so many tools out there to plan your day!&lt;/p&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%2F4hl4kfo7je601ftv772n.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%2F4hl4kfo7je601ftv772n.png" alt="pokemon ai models" width="800" height="732"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Take a moment to forgive your pet robot and plan a personal hackathon. That is what I did! A day of mindfulness can take the edge off. &lt;/p&gt;

&lt;p&gt;Put things into Motion by planning out your hackathon and grabbing a good cup of coffee or hot chocolate!&lt;/p&gt;

&lt;p&gt;The task? We are racing against our companions and trying to stretch the $5 "investment" because Wordpress is too expensive. &lt;/p&gt;

&lt;p&gt;What can be done? &lt;/p&gt;

&lt;p&gt;Pull out all your favorite frameworks and libraries. Be mindful of your AI assistant and give them a break.&lt;/p&gt;

&lt;p&gt;Respect how blazingly fast GPTs are. &lt;/p&gt;

&lt;p&gt;Skip the todo app. Skip the breadcrumbs. Skip the navigation. &lt;/p&gt;

&lt;p&gt;Skip the database. Skip the cloud functions. Skip the containers.&lt;/p&gt;

&lt;p&gt;Zen mode begins with a blank page.&lt;/p&gt;

&lt;p&gt;We need a prompt.&lt;/p&gt;

&lt;p&gt;Start with a simple command pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const commands = {
  'manage:posts': {
    'label': 'Go to posts',
    'action': ['navigate', 'https://example.com/posts']
  },
  'post:edit:1': {
    'label': 'Hello world',
    'action': ['navigate', 'https://example.com/posts/1/edit']
  },
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From there, add the binds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const open = false;

useChord(['cmd+k'],() =&amp;gt; {
  open = true;
])

useChord(['cmd+k', 'cmd+w'], () =&amp;gt; {
  open = false;
})

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

&lt;/div&gt;



&lt;p&gt;I wanted a search feature so I bootstrapped a combobox:&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;ComboBox :items="commands" onSelected="executeCommand" /&amp;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%2Fgmuff1lkoag8l7w23hq5.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%2Fgmuff1lkoag8l7w23hq5.png" alt="Command palette" width="800" height="543"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The perfect navigation features will have to wait.&lt;/p&gt;

&lt;p&gt;Zod validates my strongly typed form data.&lt;/p&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%2F4q2ynne7f0q5995nydpa.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%2F4q2ynne7f0q5995nydpa.png" alt="Zod validation" width="658" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add a new key bind to save the form.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const schema = z.object({
  thePost: z.string().min(1),
})

const form = useForm({schema}))

useChord(['cmd+s'], () =&amp;gt; {
  form.submit('https://sayless.gg')
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My prompt includes a simple markdown parsing command that uploads my markdown to the cloud. How long were you in zen mode? &lt;/p&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%2Fpbit4snj21tdesfajfur.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%2Fpbit4snj21tdesfajfur.png" alt="Example markdown post" width="800" height="865"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>hacktoberfest</category>
    </item>
    <item>
      <title>Inertia.js Does not Work</title>
      <dc:creator>Wade Zimmerman</dc:creator>
      <pubDate>Sat, 23 Mar 2024 17:19:47 +0000</pubDate>
      <link>https://dev.to/wadecodez/inertiajs-does-not-work-4pa</link>
      <guid>https://dev.to/wadecodez/inertiajs-does-not-work-4pa</guid>
      <description>&lt;p&gt;Install the javascript package from NPM. Like magic! Can you ship it to production?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install @inertiajs/react
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please follow the starting guide if you still need to do so. &lt;a href="https://inertiajs.com/client-side-setup" rel="noopener noreferrer"&gt;https://inertiajs.com/client-side-setup&lt;/a&gt;.&lt;/p&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%2Fsx7jlw2aj3nj7l9vo9s4.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%2Fsx7jlw2aj3nj7l9vo9s4.png" alt="Inertia.js Logo" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I did that, but my code does not work. The documentation is non-existent, and there needs to be more support on GitHub. &lt;/p&gt;

&lt;p&gt;You need help understanding my issue. When I submit my form, errors are everywhere, and something must be fixed! &lt;/p&gt;

&lt;p&gt;This is why I'm telling you we must use Axios or Fetch. There is simply no way to use this library. React with GraphQL is the way. &lt;/p&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%2F6dhm71thpyr1tf3ecm48.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%2F6dhm71thpyr1tf3ecm48.png" alt="Young Thug and Lil Durk Troubleshooting" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We should drop Inertia in favor of Zog and Typescript. We do not need to pull in any libraries. Everything will work out of the box.&lt;/p&gt;

&lt;p&gt;On Mastodon, everyone agrees that fetching the data only when needed is the correct approach. &lt;/p&gt;

&lt;p&gt;Let me explain the big picture. Twitter is dead. Everyone threads on Mastodon. You would need help understanding.&lt;/p&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%2Fbjv8z2jizkbe8m0hjh7j.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%2Fbjv8z2jizkbe8m0hjh7j.png" alt="tfw-too-intelligent-2smart" width="680" height="636"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hey, there is an issue on Github, and everyone is having problems. Pulling in a new library is the only way to fix this issue.&lt;/p&gt;

&lt;p&gt;What is the issue? It's multi-faceted. This library enables us to use React with page-based routing to deploy straight to the cloud. Typescript shows an error, so we must downgrade and install this library.&lt;/p&gt;

&lt;p&gt;This is why I love the Typescript community! They are so quick to resolve issues on GitHub! If we were using Vanilla Javascript...&lt;/p&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%2F8xha7fh4q05npvoksrnk.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%2F8xha7fh4q05npvoksrnk.png" alt="this is fine deep fried" width="717" height="348"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Typescript was not actually flawed. There was a bug in my code. We wanted the server to render the first-page load, so we created a function to wrap all the data into a single JSON object. &lt;/p&gt;

&lt;p&gt;The JSON object contains the name of the component that should be rendered when the browser loads. Then, all the data is passed on as props. We can server render any React component from the server. Do you need me to explain more? &lt;/p&gt;

&lt;p&gt;With this new design, we can make React work for all future projects, and legacy projects using C# and Java can be grandfathered in. After our issues with Typescript and Zog, we decided a protocol-esque approach was best. &lt;/p&gt;

&lt;p&gt;We call it Progress. 🦄&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Acknowledging &amp; Signing Fediverse Activity Requests</title>
      <dc:creator>Wade Zimmerman</dc:creator>
      <pubDate>Wed, 30 Aug 2023 23:03:16 +0000</pubDate>
      <link>https://dev.to/wadecodez/acknowledging-signing-fediverse-activity-requests-4do2</link>
      <guid>https://dev.to/wadecodez/acknowledging-signing-fediverse-activity-requests-4do2</guid>
      <description>&lt;p&gt;Read this post first: &lt;a href="https://dev.to/wadecodez/how-to-join-the-fediverse-wout-mastodon-1agh"&gt;How to Put Your Blog on the Fediverse&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you read that post, you may wonder how to participate in Fediverse activities such as sharing content, gaining followers, or accepting comments.&lt;/p&gt;

&lt;p&gt;First, you must learn to acknowledge AP requests. &lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;The Fediverse is still in its early stages. Automated tests will help find minor defects. For example, little things like date formats, JSON encoding, and base64 can all invalidate signed requests!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.w3.org/TR/activitystreams-vocabulary/#activity-types" rel="noopener noreferrer"&gt;ActivityPub Vocabulary (Data Structures)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.w3.org/TR/activitypub/" rel="noopener noreferrer"&gt;ActivityPub Spec&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-12#appendix-C.2" rel="noopener noreferrer"&gt;HTTP Signatures Draft RFC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/mastodon/mastodon/blob/e263db276fdc05dab743400977606efe98f1e949/app/controllers/concerns/signature_verification.rb" rel="noopener noreferrer"&gt;Mastodon's Signature Verification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://datatracker.ietf.org/doc/html/rfc7231#section-7.1.1.1" rel="noopener noreferrer"&gt;RFC 7231 Date Format&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://datatracker.ietf.org/doc/html/rfc4648#section-3.1" rel="noopener noreferrer"&gt;Base64 RFC&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Understanding the request flow (aka the handshake)
&lt;/h2&gt;

&lt;p&gt;ActivityPub, at its core, specifies that each user must have an inbox and an outbox. All read/write requests are handled using GET/POST methods respectively. The request flow is almost identical to email.&lt;/p&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%2Fd9z84lu47qq07kt1tiu7.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%2Fd9z84lu47qq07kt1tiu7.png" alt="ActivityPub inbox/outbox flow" width="765" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Difference between Inbox and Outbox
&lt;/h3&gt;

&lt;p&gt;The outbox supports read and write operations. Reading the outbox gives remote servers a chance to backfill. Any action made on a remote server is queued by writing to the outbox via a RESTful API. A dequeued action is then dispatched to other inboxes to be processed. Inboxes tend to be for private use only. &lt;/p&gt;

&lt;h3&gt;
  
  
  Acknowledging Requests FAQ
&lt;/h3&gt;

&lt;p&gt;To process actions, you must establish endpoints for the inbox. &lt;/p&gt;

&lt;h4&gt;
  
  
  What mime type or content type should I use?
&lt;/h4&gt;

&lt;p&gt;ActivityPub requests/responses should be &lt;code&gt;application/ld+json; profile="https://www.w3.org/ns/activitystreams&lt;/code&gt; or &lt;code&gt;application/activity+json&lt;/code&gt; unless specified otherwise. They should be treated equally. &lt;/p&gt;

&lt;h4&gt;
  
  
  My HTTP response contains the ActivityPub document; why is nothing happening?
&lt;/h4&gt;

&lt;p&gt;To acknowledge an ActivityPub request, you MUST return a POST request to the Actor's inbox. This is how ActivityPub servers remain asynchronous.&lt;/p&gt;

&lt;h4&gt;
  
  
  POST requests are not working as expected!
&lt;/h4&gt;

&lt;p&gt;Double-check you are attaching a &lt;code&gt;Content-Type&lt;/code&gt; header when sending requests, and send your request as a raw string. Some libraries/frameworks will send your data as a form request or JSON.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example Signed Acceptance/Rejection of Follow Request
&lt;/h2&gt;

&lt;p&gt;In the previous tutorial, you should have defined an endpoint for handling inbox requests and included that on your user's ActivityPub profile response. That response is critical because servers use that to find your inbox.&lt;/p&gt;

&lt;p&gt;Once you set up your server to handle incoming POST requests for your inbox. You will start to receive &lt;code&gt;application/activity+json&lt;/code&gt; type requests like the following:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@context"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://www.w3.org/ns/activitystreams"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mastodon.social/96cb3649-7a75-49c5-b246-xxxxxxxxxxxx"&lt;/span&gt;&lt;span class="p"&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;"Follow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"actor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mastodon.social/users/codoxuba"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com/activityPub/users/1"&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 the case of a &lt;code&gt;Follow&lt;/code&gt; request. The &lt;code&gt;Actor&lt;/code&gt; is the person/thing initiating the request. And the &lt;code&gt;Object&lt;/code&gt; is the person/thing that they want to follow. The &lt;code&gt;id&lt;/code&gt; is created by the server commencing the request. Most servers use the ID to track the request status. I will not be covering that in this post.&lt;/p&gt;

&lt;p&gt;However, it's important to note that &lt;code&gt;actor&lt;/code&gt;, &lt;code&gt;object&lt;/code&gt;, and &lt;code&gt;id&lt;/code&gt; can be unresolved URLs, as you see above, or they can be nested JSON-LD objects. I will not be covering that in this post.&lt;/p&gt;

&lt;h3&gt;
  
  
  Acknowledging the Incoming Activity
&lt;/h3&gt;

&lt;p&gt;This is where you should brush up on your TCP/IP protocols because the concept is similar. I will mention upfront that it's wise to not respond to requests by default. Any acknowledgment (even a reject) can be used against you in an attack. I only want to process follow requests, so I will ignore all other requests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InboxController&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Controller&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;receiveFromInternet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// todo: better validation&lt;/span&gt;
        &lt;span class="nv"&gt;$activityType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'type'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$requestType&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="s1"&gt;'Follow'&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="p"&gt;}&lt;/span&gt;

        &lt;span class="nv"&gt;$acknowledgment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s2"&gt;"@context"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"https://www.w3.org/ns/activitystreams"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s2"&gt;"summary"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Alice accepted a follow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s2"&gt;"type"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Accept"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s2"&gt;"actor"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"https://example.com/activityPub/users/1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s2"&gt;"object"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'id'&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;The acknowledgment is as simple as that. In the case of a follow request, you can choose to &lt;code&gt;Accept&lt;/code&gt;, &lt;code&gt;Reject&lt;/code&gt;, &lt;code&gt;TenativeAccept&lt;/code&gt;,  &lt;code&gt;TenativeReject&lt;/code&gt;, or ignore. The problematic part is signing the acknowledgment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Signing the Acknowledgment
&lt;/h3&gt;

&lt;p&gt;Signed requests are based on &lt;a href="https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-12#appendix-C.2" rel="noopener noreferrer"&gt;HTTP Signatures Draft RFC&lt;/a&gt;. I will be using these variables throughout the signing process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;parse_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$inboxUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;data_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'host'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;data_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'path'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$publicKeyId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'https://example.com/activityPub/users/1#main-key'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;toRfc7231String&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, encode your acknowledgment as JSON, and create a digest. You MUST encode the hash using base64 and prepend the value with the algorithm you use to generate the hash. Most Fediverse servers only support SHA-256.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$document_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$document&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$sha256&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'sha256'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$document_str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$digest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'SHA-256='&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;base64_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$sha256&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You must use that digest to create a signed &lt;code&gt;Signature&lt;/code&gt; header. Failing to include the digest will generate an invalid signature:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$dataToSign&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"(request-target): &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$method&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;host: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;date: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$date&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;digest: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$digest&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&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 data to be signed (a string) should look something like this. No trailing new lines. No carriage returns.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(request-target): post /users/@bob/inbox
host: mastadon.social
date: Wed, 30 Aug 2023 12:01:01 GMT
digest: SHA-256=MmNmMjRkYmE1ZmIwYTMwZTI2ZTgzYjJhYzViOWUyOWUxYjE2MWU1YzFmYTc0MjVlNzMwNDMzNjI5MzhiOTgyNA==
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before we can sign the message, you must generate a key pair. The public key will be distributed to the fediverse.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl genrsa &lt;span class="nt"&gt;-out&lt;/span&gt; private.pem 2048
openssl rsa &lt;span class="nt"&gt;-in&lt;/span&gt; private.pem &lt;span class="nt"&gt;-outform&lt;/span&gt; PEM &lt;span class="nt"&gt;-pubout&lt;/span&gt; &lt;span class="nt"&gt;-out&lt;/span&gt; public.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt; You must include the public key on your user's ActivityPub profile.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You must sign the headers using the same algorithm to create the digest. Pull in the OpenSSL library, depending on the programming language. With PHP, this is typically already installed as a PHP extension:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$privateKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;openssl_pkey_get_private&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Storage&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'private.pem'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="nv"&gt;$signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// mutated by openssl&lt;/span&gt;
&lt;span class="nv"&gt;$enodedSignature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;openssl_sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$privateKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;OPENSSL_ALGO_SHA256&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$encodedSignature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;base64_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$signature&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Could not sign activityPub data'&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;After signing, you will get a base64 encoded string longer than the digest generated earlier. Before including the signed data, we must build the &lt;code&gt;Signature&lt;/code&gt; header so other servers can verify the headers. If you sign extra headers, modify the &lt;code&gt;(request-target)&lt;/code&gt; section of the Signature header.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;# signature header used when signing host, date, and digest.&lt;/span&gt;
&lt;span class="nv"&gt;$signatureHeader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'keyId="%s",algorithm="rsa-sha256",headers="(request-target) host date digest",signature="%s"'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$base64&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The raw HTTP headers will look something like this when they are ready to send:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host: mastodon.social
Date: Wed, 18 Aug 2022 22:00:00 GMT
Digest: SHA-256=6ccPqhz2TJmLL08E8AFny1/Wube60hOH3g6zzwv/Ttg=
Signature: keyId="https://example.com/activityPub/users/1#main-key",algorithm="rsa-sha256",headers="(request-target) host date digest",signature="Cdih8iQQQPeDInLCN4H94Lm/hTKSNOjSnjleI8gZfndRsTwO1CqG41s+BRF2Oh51yETWEsR2ezceDgUgH+ME4jdrgUIMPm/Ox4B6c5QEASPPlFpcOfWcLryCCvEkQOVd3tbMITeY+uY6WITuZKsXREAidmDopJ2pZ3Wvk4rXuTYHZEW2vsreLYCrXDkTCm4ySL2THlOrzc0JQh/4EYRaQx+v3VqVBJvY9+qPLIm1Y9RuRoN35SMNN/IcTkxHVue+mDu6I8IIq/QVmg8kKDbwQ/ywQGzegYt+P2lKujdx0sR3gbXAHX2sTDHCKncVu/PYLJF5/LoxhVxNc3s3QEo5Bw=="
Content-Type: application/activity+json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Sending the Acknowledgement
&lt;/h3&gt;

&lt;p&gt;Finally, we can update our headers and post the message to the user's inbox:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'Host'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'Date'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'Digest'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$digest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'Signature'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$signatureHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'Content-Type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'application/activity+json'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="nv"&gt;$activityResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;withHeaders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withBody&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$document_str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'application/activity+json'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$inboxUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$document&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Wrapping Up
&lt;/h3&gt;

&lt;p&gt;After the POST request is sent successfully, you should be able to accept followers. However, keeping track of your followers will be up to you!&lt;/p&gt;

</description>
      <category>php</category>
      <category>programming</category>
      <category>webdev</category>
      <category>fediverse</category>
    </item>
    <item>
      <title>How to Put Your Blog on the Fediverse</title>
      <dc:creator>Wade Zimmerman</dc:creator>
      <pubDate>Sat, 26 Aug 2023 22:46:09 +0000</pubDate>
      <link>https://dev.to/wadecodez/how-to-join-the-fediverse-wout-mastodon-1agh</link>
      <guid>https://dev.to/wadecodez/how-to-join-the-fediverse-wout-mastodon-1agh</guid>
      <description>&lt;h2&gt;
  
  
  History/Why I should care about Mastodon or Fediverse
&lt;/h2&gt;

&lt;p&gt;Recently, there has been an influx of people who want autonomy in social media. People value their privacy and wonder why we must go to X.com, Facebook.com, or Instagram.com to socialize? Why can't we start our own social media?&lt;/p&gt;

&lt;p&gt;Only recently, the answer was that building social media is expensive and complicated. However, this is changing thanks to new W3C protocols. &lt;/p&gt;

&lt;p&gt;Mastodon takes most of the credit for adopting these new protocols because they built a clone of  X (formerly Twitter) that operates in the Fediverse.  &lt;/p&gt;

&lt;h2&gt;
  
  
  How the Fediverse Exists
&lt;/h2&gt;

&lt;p&gt;The Fediverse is a federation of servers that agree to communicate via the ActivityPub (AP) protocol. Like HTTP, SMTP, DNS, etc., the AP protocol specifies how servers should communicate to act as a social media website.&lt;/p&gt;

&lt;p&gt;For example, email protocols require all email clients/servers to adhere to a set of rules. Those rules dictate how an email message is sent and received. This makes it possible to send a message from yahoo.com to google.com to example.com without a centralized authority. &lt;/p&gt;

&lt;h3&gt;
  
  
  Fediverse FAQ
&lt;/h3&gt;

&lt;h4&gt;
  
  
  So what does this mean for social media?
&lt;/h4&gt;

&lt;p&gt;It means you can have a username like &lt;code&gt;wadecodez&lt;/code&gt; and a custom domain name like &lt;code&gt;dev.to&lt;/code&gt;, and when the handle &lt;code&gt;wadecodez@dev.to&lt;/code&gt; makes a post, it will be available to all other platforms. Post once communicate everywhere.&lt;/p&gt;

&lt;h4&gt;
  
  
  Does My Platform Need to be a Twitter Clone?
&lt;/h4&gt;

&lt;p&gt;No. Anything that follows the ActivityPub specification is valid. It could be your blog, your todo app, or something entirely different.&lt;/p&gt;

&lt;h4&gt;
  
  
  Then what is Mastodon?
&lt;/h4&gt;

&lt;p&gt;Mastodon is an implementation of the AP protocol that &lt;strong&gt;chooses&lt;/strong&gt; to behave like X (formerly Twitter). Sites that implement the AP protocol are free to exist and act however they want. Sites like &lt;code&gt;https://pxlmo.com&lt;/code&gt; and &lt;code&gt;https://peertube.tv/&lt;/code&gt; are totally separate ideas, but they can communicate with Mastodon. &lt;strong&gt;It is up to your server to respect the content of the other servers.&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Do I need to make a Mastodon instance to join the Fediverse?
&lt;/h4&gt;

&lt;p&gt;No. As long as you implement the bare minimum, your blog can be a member of the Fediverse!&lt;/p&gt;

&lt;h2&gt;
  
  
  How to create the bare minimum to join the fediverse
&lt;/h2&gt;

&lt;p&gt;Choose your language/framework of choice to receive HTTP requests. Python, Ruby, Node, PHP. Then, you'll create endpoints for discovering users, querying user profiles, and returning the contents of the inbox/outbox.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Allow for discovery
&lt;/h3&gt;

&lt;p&gt;The rest of the internet needs a way to check if your server is a member of the Fediverse. This is done according to the WebFinger protocol. When the endpoint returns a valid JSON response, with a content type of &lt;code&gt;application/jrd+json&lt;/code&gt;, other websites assume your server supports Fediverse activity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// handles https://example.com/.well-known/webfinger?resource=acct:alice@example.com&lt;/span&gt;

&lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/.well-known/webfinger'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//todo should be dynamic and validated&lt;/span&gt;

    &lt;span class="nv"&gt;$subject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'acct:alice@example.com'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$handle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="s1"&gt;'@example.com'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Str&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;between&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'acct:'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$handle&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;myJson&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s1"&gt;'subject'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'aliases'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'https://example.com/users/alice'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="s1"&gt;'links'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s2"&gt;"rel"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"http://webfinger.net/rel/profile-page"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s2"&gt;"type"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"text/html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s2"&gt;"href"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'https://example.com/users/alice'&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="s1"&gt;'rel'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'self'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'application/activity+json'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'href'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'https://example.com/activityPub/users/alice'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Query the User's profile
&lt;/h3&gt;

&lt;p&gt;The code above specifies the &lt;code&gt;https://example.com/activityPub/users/alice&lt;/code&gt; supports the &lt;code&gt;application/activity+json&lt;/code&gt; content type. When the rest of the internet sees this URL, they expect you to return an ActivityPub &lt;code&gt;Person&lt;/code&gt; type. &lt;a href="https://www.w3.org/TR/activitystreams-vocabulary/#dfn-person" rel="noopener noreferrer"&gt;See specification&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But because most websites expect this Person to be an Actor, we also need to meet the &lt;a href="https://www.w3.org/TR/activitypub/#actor-objects" rel="noopener noreferrer"&gt;specification&lt;/a&gt; for an Actor type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/activityPub/users/{user}'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="nv"&gt;$user&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="nf"&gt;myJson&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
       &lt;span class="s1"&gt;'@context'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'https://www.w3.org/ns/activitystreams'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="s1"&gt;'type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Person'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="s1"&gt;'id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'https://example.com/activityPub/users/alice'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Test User'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="s1"&gt;'preferredUsername'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'alice123'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="s1"&gt;'summary'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'my favorite food is pizza'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="s1"&gt;'inbox'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'https://example.com/activityPub/users/alice/inbox'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="s1"&gt;'outbox'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'https://example.com/activityPub/users/alice/outbox'&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="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'activityPub.user'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Return an Empty inbox and Outbox
&lt;/h3&gt;

&lt;p&gt;The last thing to do is to ensure the inbox/outbox contains data. Both boxes are an &lt;code&gt;OrderedCollection&lt;/code&gt;. &lt;a href="https://www.w3.org/TR/activitystreams-vocabulary/#dfn-orderedcollection" rel="noopener noreferrer"&gt;see spec&lt;/a&gt; When you go live these endpoints will need to acknowledge incoming POST requests. This means you need to send a POST request back to the initiator of the request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/activityPub/users/{user}/inbox'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="nv"&gt;$user&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="nf"&gt;myJson&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s1"&gt;'@context'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'https://www.w3.org/ns/activitystreams'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'summary'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Inbox for alice'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'OrderedCollection'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'totalItems'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'orderedItems'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'activityPub.inbox'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/activityPub/users/{user}/outbox'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="nv"&gt;$user&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="nf"&gt;myJson&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s1"&gt;'@context'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'https://www.w3.org/ns/activitystreams'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'summary'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Outbox for alice'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'OrderedCollection'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'totalItems'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'orderedItems'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'activityPub.outbox'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  You are now in the Fediverse!
&lt;/h2&gt;

&lt;p&gt;When you deploy your app and serve over HTTPS, you should see your profile through sites like Mastodon. &lt;strong&gt;You may have to log in to said platform to trigger account discovery.&lt;/strong&gt;&lt;/p&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%2F774y3d08x6f5h9cl88ie.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%2F774y3d08x6f5h9cl88ie.png" alt="Proof that it works" width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Now what?
&lt;/h2&gt;

&lt;p&gt;Implement other parts of the protocol, but do whatever you want. Make a blog, make a todo app, make something exiting and anything that follows the protocol will be visible throughout the Fediverse!&lt;/p&gt;

</description>
      <category>fediverse</category>
      <category>beginners</category>
      <category>programming</category>
      <category>php</category>
    </item>
    <item>
      <title>Laravel - Separate Routes into Custom Route Files</title>
      <dc:creator>Wade Zimmerman</dc:creator>
      <pubDate>Sat, 12 Aug 2023 16:43:35 +0000</pubDate>
      <link>https://dev.to/wadecodez/laravel-separate-routes-into-custom-route-files-2a49</link>
      <guid>https://dev.to/wadecodez/laravel-separate-routes-into-custom-route-files-2a49</guid>
      <description>&lt;p&gt;Have you ever lost your train of thought while looking for a route in your Laravel project?&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenario - Use Case
&lt;/h2&gt;

&lt;p&gt;Odds are you have routes for admin dashboards, user preferences, content feeds, payment gateways, etc. How do you keep all these routes organized while staying organized?&lt;/p&gt;

&lt;h2&gt;
  
  
  Answer
&lt;/h2&gt;

&lt;p&gt;Adding a custom route file is great for applying middleware and prefixes to every route in the file. The &lt;code&gt;RouteServiceProvider&lt;/code&gt; provides an interface for registering additional routers. &lt;/p&gt;

&lt;h2&gt;
  
  
  How to Add a Custom Route File in Laravel
&lt;/h2&gt;

&lt;p&gt;Open your &lt;code&gt;app/Providers/RouteServiceProvider.php&lt;/code&gt; file in your favorite editor. In the boot method, you can register custom routers:&lt;/p&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%2F4sm3kbb6g7k49reaegkt.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%2F4sm3kbb6g7k49reaegkt.png" alt="Laravel custom route file" width="800" height="674"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By default, Laravel registers a file for pages in the &lt;code&gt;web.php&lt;/code&gt; file, and API endpoints in the &lt;code&gt;api.php&lt;/code&gt; file. Each route group is assigned to a middleware group which is configurable in the &lt;code&gt;app/Http/Kernel.php&lt;/code&gt; file:&lt;/p&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%2F7w23hkbk4f3lt8evekq8.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%2F7w23hkbk4f3lt8evekq8.png" alt="Laravel Middelware Groups" width="800" height="587"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To define a custom route file like &lt;code&gt;dashboard.php&lt;/code&gt; start by adding a route group, and consider attaching a middleware group for more control:&lt;/p&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%2Ffgyj02zizd32gyfz0znx.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%2Ffgyj02zizd32gyfz0znx.png" alt="Laravel custom dashboard route file" width="800" height="585"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Defining routes for a dashboard will be scoped to the prefix and middleware defined in the &lt;code&gt;RouteServiceProvider&lt;/code&gt; class.&lt;/p&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%2Flqrgc9y624lzx0rkel3a.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%2Flqrgc9y624lzx0rkel3a.png" alt="Laravel Resource routes" width="800" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Dedicated route files is an easy way to clean up your Laravel project. For more quick Laravel tips, follow me on X &lt;a href="https://x.com/WadeCodeZ" rel="noopener noreferrer"&gt;https://x.com/WadeCodeZ&lt;/a&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>laravel</category>
    </item>
    <item>
      <title>The Next Big Milestone for AI</title>
      <dc:creator>Wade Zimmerman</dc:creator>
      <pubDate>Sat, 10 Jun 2023 16:38:15 +0000</pubDate>
      <link>https://dev.to/wadecodez/the-next-big-milestone-for-ai-2f34</link>
      <guid>https://dev.to/wadecodez/the-next-big-milestone-for-ai-2f34</guid>
      <description>&lt;p&gt;AI is turning out to have many applications such as self driving vehicles, interpretation of text, and the ability to generate media better than a human.&lt;/p&gt;

&lt;p&gt;It is easy to be critical of the work and say the end is near unless we stop the evil whatever, but I'm curious what the future will hold. Surely there will be other products and applications of AI that benefit humans.&lt;/p&gt;

&lt;p&gt;Currently we have the following types of software powered by some form of AI:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Text to text for translating and understanding large globs of text&lt;/li&gt;
&lt;li&gt;Text to media for generating human like content such as images, audio, and video&lt;/li&gt;
&lt;li&gt;Media to text for summarizing large blobs of data as a transcript.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In my opinion, I think the next big thing is going to be advancements in accessibility. Stuff like auto generating ASL for videos, or headphones that translate from Chinese to English in real time. &lt;/p&gt;

</description>
      <category>discuss</category>
      <category>ai</category>
    </item>
    <item>
      <title>Native iOS Touch Events w/ Rust</title>
      <dc:creator>Wade Zimmerman</dc:creator>
      <pubDate>Sun, 22 Jan 2023 18:21:07 +0000</pubDate>
      <link>https://dev.to/wadecodez/bevy-native-ios-touch-events-49p3</link>
      <guid>https://dev.to/wadecodez/bevy-native-ios-touch-events-49p3</guid>
      <description>&lt;p&gt;A continuation of my journey exploring cross platform game development using purely Rust and the Bevy engine.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Seriously. It's 100% Rust!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Previous Article: &lt;a href="https://dev.to/wadecodez/exploring-rust-for-native-ios-game-development-2bna"&gt;Compiling to iOS&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Criteria
&lt;/h2&gt;

&lt;p&gt;Once my project compiles to iOS, the next logical step is to handle tapping/clicking.  If all succeeds, the engine should produce some sort of coordinate value on a tap/click event. Ideally, game components should react to the event and produce some sort of visual feedback.&lt;/p&gt;




&lt;h2&gt;
  
  
  0. Create a grid of squares
&lt;/h2&gt;

&lt;p&gt;This part of the program is not important right now because my main goal is to recognize input events. If you are following along you can either skip this step or make whatever you want.&lt;/p&gt;

&lt;p&gt;For those who are curious, the core of the grid comes down to a sprite bundle which I is encapsulated as "TileBundle".  For now, each tile has an arbitrary color and position.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="n"&gt;sprite&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SpriteBundle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;sprite&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Sprite&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="py"&gt;.color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="py"&gt;.position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="nf"&gt;default&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://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%2Fwadbm4rvpd001qre521e.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%2Fwadbm4rvpd001qre521e.png" alt="Rust Native iOS Grid" width="800" height="592"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Make a System for Handling Pointer Events
&lt;/h2&gt;

&lt;p&gt;Depending on the scenario, I may want to separate click events from tap events, but for now, I want the mobile and desktop environments to behave the same. So I will dispatch the same &lt;code&gt;MyPointerEvent&lt;/code&gt; for both events.&lt;/p&gt;

&lt;p&gt;However, knowing this could be a gross simplification, I made two separate systems. If I decide to make the event handling more complex at a later point, all I have to do is expand the definition of the struct to fit my needs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nf"&gt;.add_system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tap_capture_system&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;.add_system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;click_capture_system&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="py"&gt;.add_event&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MyPointerEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;MyPointerEvent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Vec2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. Obtain a window coordinate
&lt;/h2&gt;

&lt;p&gt;I want my pointer events to hold a coordinate value. To obtain a coordinate I need to use the primary window or the window associated with a specific camera.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;click_capture_system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;windows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Res&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Windows&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;mut&lt;/span&gt; &lt;span class="n"&gt;tap_event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;EventWriter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PointerEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;q_camera&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Camera&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;GlobalTransform&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;With&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MainCamera&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;// todo: add mouse button or tap&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// assuming there is exactly one main camera entity, so query::single() is OK&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;camera_transform&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;q_camera&lt;/span&gt;&lt;span class="nf"&gt;.single&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// get the window that the camera is displaying to (or the primary window)&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;wnd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nn"&gt;RenderTarget&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Window&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;camera&lt;/span&gt;&lt;span class="py"&gt;.target&lt;/span&gt;       
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;windows&lt;/span&gt;&lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;windows&lt;/span&gt;&lt;span class="nf"&gt;.get_primary&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. Dispatch shared event
&lt;/h2&gt;

&lt;p&gt;Now I need to listen for click events or pointer events on the window. When an event happens in either scenario, I will dispatch the same event.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;click_capture_system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="n"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Res&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MouseButton&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// check if the cursor is inside the window and get its position&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;screen_pos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wnd&lt;/span&gt;&lt;span class="nf"&gt;.cursor_position&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;btn&lt;/span&gt;&lt;span class="nf"&gt;.just_released&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;MouseButton&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Left&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nd"&gt;debug!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello click {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;screen_pos&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;tap_event&lt;/span&gt;&lt;span class="nf"&gt;.send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PointerEvent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;screen_pos&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;Touches are handled slightly different because the position is not optional. Pretty sure the difference is attributed to the fact that mobile devices can only detect taps when the application is open. That's a whole can of worms I don't really care about right now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;tap_capture_system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="n"&gt;touches&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Res&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Touches&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;touch&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;touches&lt;/span&gt;&lt;span class="nf"&gt;.iter_just_released&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;touches&lt;/span&gt;&lt;span class="nf"&gt;.just_released&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;touch&lt;/span&gt;&lt;span class="nf"&gt;.id&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nd"&gt;debug!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello tap {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;touch&lt;/span&gt;&lt;span class="nf"&gt;.position&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
            &lt;span class="n"&gt;tap_event&lt;/span&gt;&lt;span class="nf"&gt;.send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PointerEvent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;touch&lt;/span&gt;&lt;span class="nf"&gt;.position&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;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%2Fvqe93lq3qp4vm2gze1z2.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%2Fvqe93lq3qp4vm2gze1z2.png" alt="Rust native tap event logged to console" width="800" height="806"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Listening to custom event
&lt;/h2&gt;

&lt;p&gt;Now I want to consume my custom event data elsewhere in my application.  So I for now I created a separate sub system for handling pointer events which lives within the plugin I use for my TileBundle code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;handle_tile_pointer_events&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;EventReader&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PointerEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;pointer_event&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="nf"&gt;.iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="c1"&gt;// do something&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  5. Convert Screen Coordinates into Game World Coordinates
&lt;/h2&gt;

&lt;p&gt;This took me a while to conceptualize the first time so if give yourself some time to understand what is going on. &lt;/p&gt;

&lt;p&gt;There is a problem with my existing code. There are multiple coordinate systems to account for. The window/mobile device have a 2D coordinate system and the game has a 3D/2D coordinate system.&lt;/p&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%2Faxudd649l0b4e4invavj.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%2Faxudd649l0b4e4invavj.png" alt="NDC Ray Casting Rust" width="520" height="275"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I need to convert the operating system's coordinate system into a coordinate that makes since for my game. This is done by ray casting, aka normalizing coordinates. I like to think of it as mapping the game world onto a flat surface.&lt;/p&gt;

&lt;p&gt;Here is the code I'm using for the mouse system and the tap system.  I replaced the raw touch/click position in previous steps with the world position produced below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// get the size of the window&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;window_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Vec2&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wnd&lt;/span&gt;&lt;span class="nf"&gt;.width&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;f32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wnd&lt;/span&gt;&lt;span class="nf"&gt;.height&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;f32&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// convert screen position [0..resolution] to ndc [-1..1] (gpu coordinates)&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;ndc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;INSERT_SCREEN_POSITION_HERE&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;window_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nn"&gt;Vec2&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ONE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// matrix for undoing the projection and camera transform&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;ndc_to_world&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;camera_transform&lt;/span&gt;&lt;span class="nf"&gt;.compute_matrix&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;camera&lt;/span&gt;&lt;span class="nf"&gt;.projection_matrix&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.inverse&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// use it to convert ndc to world-space coordinates&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;world_pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ndc_to_world&lt;/span&gt;&lt;span class="nf"&gt;.project_point3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ndc&lt;/span&gt;&lt;span class="nf"&gt;.extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// reduce it to a 2D value&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;world_pos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Vec2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;world_pos&lt;/span&gt;&lt;span class="nf"&gt;.truncate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  6. Modify the world position for iOS.
&lt;/h2&gt;

&lt;p&gt;Not sure if this is something that will change in the future, but currently Bevy produces an upside down coordinate.  To fix this problem, I flipped the world position above on the Y axis.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;world_pos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Vec2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;world_pos&lt;/span&gt;&lt;span class="nf"&gt;.truncate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;// flip y axis so touches line up with screen&lt;/span&gt;
    &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nn"&gt;Vec2&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  7. Consume reusable pointer event
&lt;/h2&gt;

&lt;p&gt;I used the event to change the tapped/clicked tile to a random color. &lt;/p&gt;

&lt;p&gt;I had to manually import &lt;code&gt;nalgebra&lt;/code&gt; and &lt;code&gt;parry2d&lt;/code&gt; to make this work. &lt;/p&gt;

&lt;p&gt;For now, I calculate the box collider on each event. Ideally these coordinates would be held by the tile bundle. All I'm doing is seeing if the click/tap overlaps with a tile sprite.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="nf"&gt;.iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;tile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;sprite&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;global&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="nf"&gt;.iter_mut&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;tile&lt;/span&gt;&lt;span class="nf"&gt;.update&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;pointer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;point!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="py"&gt;.position.x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="py"&gt;.position.y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;global&lt;/span&gt;&lt;span class="nf"&gt;.translation&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="py"&gt;.scale&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;bl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;tr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;square&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="nd"&gt;point!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bl&lt;/span&gt;&lt;span class="py"&gt;.x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tr&lt;/span&gt;&lt;span class="py"&gt;.y&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nd"&gt;point!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tr&lt;/span&gt;&lt;span class="py"&gt;.x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tr&lt;/span&gt;&lt;span class="py"&gt;.y&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nd"&gt;point!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tr&lt;/span&gt;&lt;span class="py"&gt;.x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bl&lt;/span&gt;&lt;span class="py"&gt;.y&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nd"&gt;point!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bl&lt;/span&gt;&lt;span class="py"&gt;.x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bl&lt;/span&gt;&lt;span class="py"&gt;.y&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;point_in_poly2d&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;square&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;sprite&lt;/span&gt;&lt;span class="py"&gt;.color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;random_color&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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

&lt;p&gt;It is possible to use Rust to handle tap events in iOS.&lt;/p&gt;

&lt;p&gt;Here is what the final program looks like.&lt;/p&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%2Fy97h5l7b4ionvs0eyfxc.gif" 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%2Fy97h5l7b4ionvs0eyfxc.gif" alt="Color changing iOS Grid on Tap" width="1280" height="720"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  To Be Continued
&lt;/h2&gt;

&lt;p&gt;Please consider leaving a like and comment below. It helps me plan the next article. &lt;/p&gt;

&lt;p&gt;Plus commenting what you're working on may inspire or help others. Let's make Rust iOS development a thing! &lt;/p&gt;

</description>
      <category>rust</category>
      <category>ios</category>
      <category>gamedev</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
