<?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: Nexa Tech</title>
    <description>The latest articles on DEV Community by Nexa Tech (@tech_nexa_8e64b11ae4f24f9).</description>
    <link>https://dev.to/tech_nexa_8e64b11ae4f24f9</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3560711%2Fdfd36866-d18e-4b54-8c3a-18b1c5fd2ede.jpg</url>
      <title>DEV Community: Nexa Tech</title>
      <link>https://dev.to/tech_nexa_8e64b11ae4f24f9</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tech_nexa_8e64b11ae4f24f9"/>
    <language>en</language>
    <item>
      <title>AI can design an app now. Are designers redundant?</title>
      <dc:creator>Nexa Tech</dc:creator>
      <pubDate>Sat, 10 Oct 2026 01:19:01 +0000</pubDate>
      <link>https://dev.to/tech_nexa_8e64b11ae4f24f9/ai-can-design-an-app-now-are-designers-redundant-1ie4</link>
      <guid>https://dev.to/tech_nexa_8e64b11ae4f24f9/ai-can-design-an-app-now-are-designers-redundant-1ie4</guid>
      <description>&lt;p&gt;AI can now draft a screen, write the button labels, and turn a sketch into a clickable prototype in minutes. So here is the question every designer has quietly asked at 2 a.m.: &lt;strong&gt;are designers now redundant?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To answer it honestly, we have to separate two things people often mix up: &lt;em&gt;producing design artefacts&lt;/em&gt; and &lt;em&gt;doing design&lt;/em&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI automates the output of design—screens, copy, prototypes—not the decisions behind it.&lt;/li&gt;
&lt;li&gt;When producing a draft becomes cheap, the bottleneck moves to judgement, context, and validation.&lt;/li&gt;
&lt;li&gt;Tasks inside the designer’s job will disappear. The job itself will move up a level.&lt;/li&gt;
&lt;li&gt;The designers at real risk are the ones whose work stops at the artefact.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;&lt;em&gt;AI can make the options quickly. Design is deciding which one is right for the people who will use it.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What design actually is
&lt;/h2&gt;

&lt;p&gt;From the outside, design looks like making screens. In practice, a screen is the last and smallest part of the work. A typical app feature passes through five layers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Problem:&lt;/strong&gt; Who is the user, what are they trying to do, and what gets in their way?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Priority:&lt;/strong&gt; Of everything we could build, what matters first, and what do we leave out?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flow:&lt;/strong&gt; In what order do things happen, and what happens when something goes wrong?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interface:&lt;/strong&gt; Layout, components, hierarchy, copy, and visual style.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validation:&lt;/strong&gt; Does it work for real people, in real conditions? What do we change?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each layer depends on the one before it. A beautiful interface built on the wrong problem is still a failure—just a better-looking one.&lt;/p&gt;

&lt;h2&gt;
  
  
  What AI is actually good at
&lt;/h2&gt;

&lt;p&gt;Map today’s AI tools onto those layers and a clear pattern appears:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Where AI is strong&lt;/th&gt;
&lt;th&gt;What it helps with&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Interface&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Drafting layouts, generating variations, writing microcopy, applying a design system, and building quick prototypes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Problem and validation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Summarising reviews and interviews, grouping feedback, and spotting patterns across lots of text.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Priority and flow&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Still weak at choosing trade-offs, deciding what to cut, and knowing which edge case will hurt users.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;AI is strongest at the layer that is easiest to see and weakest at the layers that decide whether a product succeeds. That is why it can look like it replaces designers, while in practice it replaces only part of what they do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four reasons designers aren’t redundant
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Cheap drafts make choosing more important
&lt;/h3&gt;

&lt;p&gt;When one screen took a day, a team might compare two options. When AI produces twenty in a minute, someone has to decide which one is right—and why.&lt;/p&gt;

&lt;p&gt;Abundance does not remove the need for judgement; it multiplies it. The scarce skill is no longer making options. It is choosing well between them.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. AI doesn’t know your user’s context
&lt;/h3&gt;

&lt;p&gt;A model learns from patterns across millions of apps. It has never stood in a supermarket aisle holding a basket in one hand and a phone in the other, which is exactly the situation our Ingredient Scanner has to be designed for.&lt;/p&gt;

&lt;p&gt;Context like that changes where buttons go, how big text is, and what appears first. It comes from observing users, not from training data.&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;A real use case has constraints that a generic prompt cannot see: one hand is busy, the aisle is distracting, and the answer needs to be immediate.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. AI drifts toward the average
&lt;/h3&gt;

&lt;p&gt;Generative models produce the most likely answer. In design, the most likely answer is the most common one. That is useful for standard patterns such as login screens or settings, but it is the opposite of what makes a product memorable.&lt;/p&gt;

&lt;p&gt;Differentiation is a deliberate choice to &lt;em&gt;not&lt;/em&gt; do the average thing, and someone has to make it.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Someone has to own the result
&lt;/h3&gt;

&lt;p&gt;If a flow confuses older users, if a screen fails accessibility, or if a dark pattern slips into checkout, the question is not “which tool generated this?” but “who approved it?”&lt;/p&gt;

&lt;p&gt;Responsibility for the user’s experience cannot be delegated to a model. It needs a person who notices, cares, and can explain the decision.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;AI is great at making options. Design is the job of choosing.&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;&lt;em&gt;Good review connects interface choices to evidence: what users said, what they can access, and what the product needs to achieve.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The strongest counter-argument
&lt;/h2&gt;

&lt;p&gt;To be fair, the case for “designers are redundant” is not silly. It goes like this: &lt;em&gt;most apps do not need original design. They need a decent, standard interface, fast. AI already does that well enough.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is partly true. For internal tools, simple prototypes, or early MVPs, a developer with a good design system and an AI tool can ship something perfectly usable without a dedicated designer. Some design work—resizing assets, making variants, or writing first-draft copy—will shrink for real.&lt;/p&gt;

&lt;p&gt;But that argument proves something narrower than it claims. It shows that &lt;strong&gt;certain design tasks&lt;/strong&gt; are being automated, not that &lt;strong&gt;the design role&lt;/strong&gt; is. As soon as a product competes on experience, serves a specific audience, or carries real risk, the upper layers—problem, priority, flow, and validation—come back. Those are human work.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually changes for designers
&lt;/h2&gt;

&lt;p&gt;What becomes more valuable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User research and observing real behaviour&lt;/li&gt;
&lt;li&gt;Framing problems and setting constraints&lt;/li&gt;
&lt;li&gt;Building and maintaining a strong design system that AI can follow&lt;/li&gt;
&lt;li&gt;Critiquing and editing, not just creating&lt;/li&gt;
&lt;li&gt;Accessibility, ethics, and edge cases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What becomes less valuable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Producing screens from a clear spec&lt;/li&gt;
&lt;li&gt;Making many variants by hand&lt;/li&gt;
&lt;li&gt;Pixel-perfect mockups that are never tested&lt;/li&gt;
&lt;li&gt;Design work that stops at the hand-off&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How we work with AI at Nexa Tech
&lt;/h2&gt;

&lt;p&gt;Our rule is simple: AI speeds up the parts we can easily check, and people own the parts that need judgement.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Research:&lt;/strong&gt; AI groups app-store reviews and feedback into themes; a designer reads the raw quotes before drawing conclusions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exploration:&lt;/strong&gt; AI generates many layout options; the team picks a direction based on the user’s context, not what looks best in isolation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production:&lt;/strong&gt; AI drafts screens from our components and tokens; a designer fixes hierarchy, spacing, and states.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validation:&lt;/strong&gt; Real users test the prototype. AI can predict behaviour, but only people can show it.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Start with your design system.&lt;/strong&gt; AI follows whatever rules you give it. A clear, documented design system turns its output from “generic app” into “close to ours,” and makes every review faster.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  So, is the designer redundant?
&lt;/h2&gt;

&lt;p&gt;No. But the designer who only pushes pixels might be.&lt;/p&gt;

&lt;p&gt;AI has made the visible part of design cheap. That forces the invisible part—understanding people and choosing what to build for them—into the spotlight. Designers who move up to that level will not be replaced by AI. They will be the ones deciding how to use it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Full Story: &lt;a href="https://www.nexateam.dev/blog/ai-in-app-design" rel="noopener noreferrer"&gt;https://www.nexateam.dev/blog/ai-in-app-design&lt;/a&gt;
&lt;/h2&gt;

</description>
      <category>design</category>
      <category>ai</category>
      <category>ux</category>
      <category>discuss</category>
    </item>
    <item>
      <title>AI in software development: turn change into an advantage</title>
      <dc:creator>Nexa Tech</dc:creator>
      <pubDate>Fri, 09 Oct 2026 15:06:09 +0000</pubDate>
      <link>https://dev.to/tech_nexa_8e64b11ae4f24f9/ai-in-software-development-turn-change-into-an-advantage-35gc</link>
      <guid>https://dev.to/tech_nexa_8e64b11ae4f24f9/ai-in-software-development-turn-change-into-an-advantage-35gc</guid>
      <description>&lt;p&gt;"Will AI replace developers?" is the wrong question. The better one is: &lt;strong&gt;how should we change the way we work?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AI's biggest gift isn't faster typing. It gives teams more room to understand the problem, test their decisions, and build products people actually want to use.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI should extend what your team can do, not let anyone skip the thinking.&lt;/li&gt;
&lt;li&gt;Start with repetitive work where the result is easy to check.&lt;/li&gt;
&lt;li&gt;Put guardrails in place before you speed up: testing, security, and review apply to AI output too.&lt;/li&gt;
&lt;li&gt;Measure product outcomes, not lines of code.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The work hasn't shrunk. It has moved.
&lt;/h2&gt;

&lt;p&gt;An engineer can now ask AI to sketch a feature, explain an unfamiliar codebase, write test cases, draft API docs, or summarise a production incident. Tasks that used to break a whole afternoon of focus now take minutes.&lt;/p&gt;

&lt;p&gt;That doesn't make the engineer's job smaller. It moves the most important work to the front:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;choosing the right problem&lt;/li&gt;
&lt;li&gt;setting the right constraints&lt;/li&gt;
&lt;li&gt;weighing the trade-offs&lt;/li&gt;
&lt;li&gt;owning the experience users end up with&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;AI can generate options. People still have to choose the one worth building.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The trap: speed that turns out to be debt
&lt;/h2&gt;

&lt;p&gt;AI-generated code can look completely convincing while resting on a bad assumption, missing an edge case, or calling an API that no longer fits. A team that only tracks how much faster it ships is easy to fool. What feels like productivity can be technical debt that shows up later.&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ Use AI as a lever
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Ask it to state its assumptions and risks before it proposes a solution&lt;/li&gt;
&lt;li&gt;Review the diff, the tests, and the user-facing behaviour like any other change&lt;/li&gt;
&lt;li&gt;Save the prompts, decisions, and results that worked so the whole team learns from them&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ❌ Don't use AI as a shortcut
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Shipping generated code you don't understand&lt;/li&gt;
&lt;li&gt;Pasting customer data or secrets into tools that haven't been approved&lt;/li&gt;
&lt;li&gt;Letting fast code generation replace discovery and product thinking&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How we adopt AI at Nexa Tech
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Start small&lt;/strong&gt;&lt;br&gt;
Pick repetitive work that's easy to verify: unit tests for pure functions, pull-request summaries, fixtures, or turning an issue into a checklist. The value is real and the risk is low.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Set guardrails first&lt;/strong&gt;&lt;br&gt;
Decide which data never leaves your systems, who reviews AI output, what the minimum test coverage is, and when you roll back. Clear rules are what let a team use AI with confidence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Give AI context, not just prompts&lt;/strong&gt;&lt;br&gt;
AI does better work when it knows your conventions, your architecture, and your definition of done. Keep the README, decision records, good examples, and test suite up to date. They help new teammates and AI alike.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Measure outcomes&lt;/strong&gt;&lt;br&gt;
Track lead time, post-release defects, review time, and user satisfaction. If a tool adds lines of code but none of these numbers improve, it hasn't given you a real advantage.&lt;/p&gt;

&lt;h2&gt;
  
  
  The skills that are worth more now
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Skill&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;01&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Asking better questions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Breaking a vague problem into constraints, success criteria, and steps you can verify.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;02&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Technical judgement&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Spotting the answer that is &lt;em&gt;almost&lt;/em&gt; right but doesn't fit your system, your data, or your users.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;03&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Systems thinking&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Weighing product, operations, security, and experience together so the decision holds up over time.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Make AI a team advantage, not a personal trick
&lt;/h2&gt;

&lt;p&gt;The strongest team isn't the one with the most AI tools. It's the one that turns what each person knows into something the whole team can use: prompt patterns for familiar tasks, review checklists, component libraries, reliable tests, and regular, honest talks about what worked and what didn't.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Pair before you automate&lt;/strong&gt;&lt;br&gt;
For the first few weeks, let AI suggest and let people decide. Note where it saves time, where it adds noise, and why. Once you have that evidence, you'll know what is safe to automate.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  One question to ask every time
&lt;/h2&gt;

&lt;p&gt;Before accepting any AI output, ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"If AI weren't here, how would I check this?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you can still read the code, run the tests, inspect the data, and explain the choice to a teammate, AI is making you stronger. If you can't, it is hiding a gap.&lt;/p&gt;




&lt;p&gt;AI isn't the enemy of good engineering. It raises the bar. Good engineers will be judged less by how fast they type and more by how well they turn complex problems into products people can trust.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How is your team using AI in its workflow? What has worked, and what hasn't? Let me know in the comments 👇&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://www.nexateam.dev/blog/ai-trong-phat-trien-phan-mem" rel="noopener noreferrer"&gt;Nexa Tech blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>productivity</category>
      <category>software</category>
    </item>
    <item>
      <title>Building quishing protection into a React Native QR scanner (no server needed)</title>
      <dc:creator>Nexa Tech</dc:creator>
      <pubDate>Fri, 09 Oct 2026 12:04:31 +0000</pubDate>
      <link>https://dev.to/tech_nexa_8e64b11ae4f24f9/building-quishing-protection-into-a-react-native-qr-scanner-no-server-needed-1pb3</link>
      <guid>https://dev.to/tech_nexa_8e64b11ae4f24f9/building-quishing-protection-into-a-react-native-qr-scanner-no-server-needed-1pb3</guid>
      <description>&lt;p&gt;ScanQR is on &lt;a href="https://apps.apple.com/vn/app/scanqr-qr-code-barcode/id6779862851" rel="noopener noreferrer"&gt;App Store&lt;/a&gt; and &lt;a href="https://play.google.com/store/apps/details?id=com.nexa.qr_code" rel="noopener noreferrer"&gt;Google Play&lt;/a&gt;. I'd love feedback, especially regional phishing patterns or payment formats I should add next. 🙏&lt;/p&gt;

&lt;p&gt;Most QR scanner apps do one thing: decode the string and throw it at the browser. That's precisely what &lt;strong&gt;quishing&lt;/strong&gt; (QR phishing) relies on. A sticker over a parking meter, a fake "pay here" code on a restaurant table, a parcel notice with a code. You can't hover over a QR code to preview the link.&lt;/p&gt;

&lt;p&gt;I'm building &lt;strong&gt;ScanQR&lt;/strong&gt;, a React Native scanner. For this release I wanted it to &lt;em&gt;understand&lt;/em&gt; what it scans before the user taps anything. Here's how two parts of that work: link safety checks and payment QR parsing.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Link safety: offline heuristics first
&lt;/h2&gt;

&lt;p&gt;I didn't want every scan to need a network round trip, so the first pass is pure string analysis that runs instantly and offline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;RiskReason&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shortener&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;       &lt;span class="c1"&gt;// bit.ly, qrco.de… hides the real destination&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;punycode&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;        &lt;span class="c1"&gt;// xn-- labels: look-alike characters&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ip_host&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;         &lt;span class="c1"&gt;// http://185.23.x.x/login&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;userinfo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;        &lt;span class="c1"&gt;// https://paypal.com@evil.site&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;insecure&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;        &lt;span class="c1"&gt;// plain http&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;suspicious_tld&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;  &lt;span class="c1"&gt;// .zip, .mov, .top, .xyz…&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;impersonation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;   &lt;span class="c1"&gt;// paypal-secure-login.com&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;many_subdomains&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;malicious&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// remote verdict&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few of these are more interesting than they look.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The &lt;code&gt;@&lt;/code&gt; trick.&lt;/strong&gt; In &lt;code&gt;https://paypal.com@evil.site/&lt;/code&gt;, everything before &lt;code&gt;@&lt;/code&gt; is &lt;em&gt;userinfo&lt;/em&gt;, so the real host is &lt;code&gt;evil.site&lt;/code&gt;. I pull the host from the authority section and take what comes after the &lt;strong&gt;last&lt;/strong&gt; &lt;code&gt;@&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;authority&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;authority&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lastIndexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hostPort&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;at&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="nx"&gt;authority&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;at&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;authority&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Brand impersonation.&lt;/strong&gt; I look for a brand name anywhere in the host (with dots and dashes removed), &lt;em&gt;unless&lt;/em&gt; the registrable domain belongs to that brand:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;registrableLabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;host&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;          &lt;span class="c1"&gt;// "example" for a.b.example.co.uk&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;flat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;-.&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;official&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;BRANDS&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="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;flat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;official&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;reasons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;impersonation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So &lt;code&gt;paypal-secure.verify-account.xyz&lt;/code&gt; gets flagged, but &lt;code&gt;www.paypal.com&lt;/code&gt; and &lt;code&gt;paypal.co.uk&lt;/code&gt; pass. The brand list includes local targets too (Vietcombank, Techcombank, Shopee, Lazada), because phishing is regional.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Severity, not a yes/no answer.&lt;/strong&gt; Some signals are near-certain red flags. Others are only "be careful":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DANGER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;userinfo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;punycode&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;impersonation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ip_host&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;reasons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;DANGER&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;danger&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;reasons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;insecure&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;many_subdomains&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;reasons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;caution&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;safe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A plain &lt;code&gt;http://&lt;/code&gt; link alone doesn't deserve a red banner. Crying wolf trains users to ignore warnings.&lt;/p&gt;

&lt;h3&gt;
  
  
  Then, optionally, ask Google
&lt;/h3&gt;

&lt;p&gt;On top of the local result, the app can query &lt;strong&gt;Google Web Risk&lt;/strong&gt; (&lt;code&gt;uris:search&lt;/code&gt;) with a 5-second timeout. It's merged in only if it returns a verdict. If the device is offline or the request times out, the local result still stands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;withRemoteVerdict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UrlRisk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;malicious&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&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="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;malicious&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="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;danger&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;reasons&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;malicious&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reasons&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="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Payment QRs: one parser, many countries
&lt;/h2&gt;

&lt;p&gt;VietQR, PromptPay, PayNow, DuitNow, QRIS and Pix are all built on the &lt;strong&gt;EMVCo Merchant-Presented QR&lt;/strong&gt; spec. The payload is a flat TLV string: 2-digit tag, 2-digit length, value. The parser fits in a few lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parseTlv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;4&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="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isInteger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;len&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;len&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;len&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;len&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;out&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;Tag &lt;code&gt;54&lt;/code&gt; is the amount, &lt;code&gt;59&lt;/code&gt; the merchant name and &lt;code&gt;60&lt;/code&gt; the city. Nested templates like &lt;code&gt;38&lt;/code&gt; (VietQR) or &lt;code&gt;62&lt;/code&gt; (additional data) are TLV strings themselves, so you just call &lt;code&gt;parseTlv&lt;/code&gt; again.&lt;/p&gt;

&lt;p&gt;Every EMVCo payload ends with &lt;code&gt;6304&lt;/code&gt; plus a &lt;strong&gt;CRC-16/CCITT-FALSE&lt;/strong&gt; checksum. Checking it costs almost nothing and catches damaged or edited codes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;crc16&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;crc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mh"&gt;0xffff&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;crc&lt;/span&gt; &lt;span class="o"&gt;^=&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;charCodeAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;8&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;crc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crc&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0x8000&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;crc&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="mh"&gt;0x1021&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0xffff&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;crc&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0xffff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;crc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;padStart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;crcValid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;crc16&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the checksum fails, the user sees &lt;em&gt;"QR may be damaged (checksum mismatch)"&lt;/em&gt;. They're about to send money, so that warning matters. UPI, SEPA (EPC) and Swiss QR-bill use different formats. They get their own parsers, and IBANs are checked with mod-97.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. When you generate codes, check them too
&lt;/h2&gt;

&lt;p&gt;People love custom colors and a logo in the middle of their QR code, and then nobody can scan it. After rendering, ScanQR scans its own output and shows &lt;em&gt;"Scannable — verified"&lt;/em&gt; or a warning. It also warns about low contrast, inverted colors and a logo that's too large.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;React Native 0.81 (New Architecture), Reanimated 4&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;react-native-vision-camera&lt;/code&gt; + ML Kit barcode scanning&lt;/li&gt;
&lt;li&gt;Open Food Facts / Open Beauty Facts / Open Library for product barcodes&lt;/li&gt;
&lt;li&gt;i18next with 23 languages&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Lessons
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Do the work offline first.&lt;/strong&gt; The local heuristics give an answer instantly, and the network check only adds to it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graded warnings beat yes/no.&lt;/strong&gt; Too many red banners and users stop reading them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Checksums are free safety.&lt;/strong&gt; If a format has one (EMVCo CRC, IBAN mod-97), check it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ScanQR is on &lt;a href="https://apps.apple.com/vn/app/scanqr-qr-code-barcode/id6779862851" rel="noopener noreferrer"&gt;App Store&lt;/a&gt; and &lt;a href="https://play.google.com/store/apps/details?id=com.nexa.qr_code" rel="noopener noreferrer"&gt;Google Play&lt;/a&gt;. I'd love feedback, especially regional phishing patterns or payment formats I should add next. 🙏&lt;/p&gt;

</description>
      <category>ai</category>
      <category>react</category>
      <category>reactnative</category>
      <category>security</category>
    </item>
    <item>
      <title>I Got Tired of Losing Context Behind My Work Photos, So I Built FieldStamp</title>
      <dc:creator>Nexa Tech</dc:creator>
      <pubDate>Fri, 17 Jul 2026 14:14:47 +0000</pubDate>
      <link>https://dev.to/tech_nexa_8e64b11ae4f24f9/i-got-tired-of-losing-context-behind-my-work-photos-so-i-built-fieldstamp-56pg</link>
      <guid>https://dev.to/tech_nexa_8e64b11ae4f24f9/i-got-tired-of-losing-context-behind-my-work-photos-so-i-built-fieldstamp-56pg</guid>
      <description>&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa24xkiw8l25myx1x2v9h.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa24xkiw8l25myx1x2v9h.png" alt=" " width="800" height="391"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  FieldStamp Link
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Android:&lt;/strong&gt; &lt;a href="https://play.google.com/store/apps/details?id=com.fieldstamp.free" rel="noopener noreferrer"&gt;FieldStamp GPS Camera on Google Play&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;iPhone:&lt;/strong&gt; &lt;a href="https://apps.apple.com/us/app/fieldstamp-gps-camera/id6786527961" rel="noopener noreferrer"&gt;FieldStamp GPS Camera on the App Store&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A few months ago, I noticed something annoying about the photos on my phone.&lt;/p&gt;

&lt;p&gt;I had a lot of them.&lt;/p&gt;

&lt;p&gt;Photos from different places. Different days. Different projects. Some were taken just a few hours apart, so they looked almost the same.&lt;/p&gt;

&lt;p&gt;At the time I took them, I knew exactly what each photo was for.&lt;/p&gt;

&lt;p&gt;A few days later, I did not.&lt;/p&gt;

&lt;p&gt;I would open my gallery and ask myself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Was this taken at the first location or the second one?&lt;/li&gt;
&lt;li&gt;What time did I take this?&lt;/li&gt;
&lt;li&gt;Which project did this belong to?&lt;/li&gt;
&lt;li&gt;Why did I take this photo again?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The photo was still there, but most of the useful context was gone.&lt;/p&gt;

&lt;p&gt;That problem became the starting point for &lt;strong&gt;FieldStamp GPS Camera&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Photo Is Not Always Enough
&lt;/h2&gt;

&lt;p&gt;For personal photos, this is usually not a big issue.&lt;/p&gt;

&lt;p&gt;You take a photo, keep it in your gallery, and maybe look at it again later.&lt;/p&gt;

&lt;p&gt;But work photos are different.&lt;/p&gt;

&lt;p&gt;People working in construction, maintenance, property inspections, agriculture, utilities, or field services often take photos as evidence.&lt;/p&gt;

&lt;p&gt;The photo needs to show more than what the camera can see.&lt;/p&gt;

&lt;p&gt;It may also need to answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Where was this taken?&lt;/li&gt;
&lt;li&gt;When was it taken?&lt;/li&gt;
&lt;li&gt;Which job or project was it related to?&lt;/li&gt;
&lt;li&gt;What issue was being documented?&lt;/li&gt;
&lt;li&gt;Can this photo be included in a report?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without that information, someone usually has to remember it, write it down, rename the file, or organize everything manually later.&lt;/p&gt;

&lt;p&gt;That sounds manageable when there are five photos.&lt;/p&gt;

&lt;p&gt;It becomes painful when there are fifty.&lt;/p&gt;

&lt;h2&gt;
  
  
  My First Idea Was Very Simple
&lt;/h2&gt;

&lt;p&gt;At first, I only wanted to add location and time directly to a photo.&lt;/p&gt;

&lt;p&gt;Take a photo, display the GPS coordinates, address, date, and time on it, then save it.&lt;/p&gt;

&lt;p&gt;Simple.&lt;/p&gt;

&lt;p&gt;But while building the app, I realized that the watermark itself did not solve the full problem.&lt;/p&gt;

&lt;p&gt;The next problem was organization.&lt;/p&gt;

&lt;p&gt;Even if every photo had GPS information, users could still end up with hundreds of images mixed together.&lt;/p&gt;

&lt;p&gt;So I added projects.&lt;/p&gt;

&lt;p&gt;A user could create a project, select it before taking photos, and keep related photos in one place.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Apartment inspection&lt;/li&gt;
&lt;li&gt;Construction Site A&lt;/li&gt;
&lt;li&gt;Roof repair&lt;/li&gt;
&lt;li&gt;Farm Area 2&lt;/li&gt;
&lt;li&gt;Monthly equipment check&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This made the app feel less like a normal camera and more like a small field documentation tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Details That Became Important Later
&lt;/h2&gt;

&lt;p&gt;While testing the app, I started noticing small things that I had not considered in the beginning.&lt;/p&gt;

&lt;p&gt;GPS accuracy was one of them.&lt;/p&gt;

&lt;p&gt;A phone may detect a location, but that does not always mean the location is accurate.&lt;/p&gt;

&lt;p&gt;Sometimes the signal is good. Sometimes it is not.&lt;/p&gt;

&lt;p&gt;So the app shows GPS accuracy before the photo is taken. The user can wait a little longer when an accurate location is important.&lt;/p&gt;

&lt;p&gt;I also added information such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Coordinates&lt;/li&gt;
&lt;li&gt;Address&lt;/li&gt;
&lt;li&gt;Date and time&lt;/li&gt;
&lt;li&gt;GPS accuracy&lt;/li&gt;
&lt;li&gt;Altitude&lt;/li&gt;
&lt;li&gt;Compass direction&lt;/li&gt;
&lt;li&gt;Project name&lt;/li&gt;
&lt;li&gt;Custom notes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Users do not need to display everything. They can choose a layout depending on what matters for their work.&lt;/p&gt;

&lt;p&gt;Some people may only need the date and address.&lt;/p&gt;

&lt;p&gt;Others may need coordinates, project information, and a note describing the issue.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Photo Was Only Half of the Workflow
&lt;/h2&gt;

&lt;p&gt;Another thing I learned was that taking the photo is often not the final step.&lt;/p&gt;

&lt;p&gt;After a site visit or inspection, the photos may need to be sent to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A manager&lt;/li&gt;
&lt;li&gt;A client&lt;/li&gt;
&lt;li&gt;A property owner&lt;/li&gt;
&lt;li&gt;A contractor&lt;/li&gt;
&lt;li&gt;A project team&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Copying photos one by one into a document takes time, especially when each image also needs a description.&lt;/p&gt;

&lt;p&gt;That is why I added PDF reports.&lt;/p&gt;

&lt;p&gt;Users can select their field photos and create a report containing the images, project information, location details, and notes.&lt;/p&gt;

&lt;p&gt;It is still a simple feature, but it removes a repetitive step from the workflow.&lt;/p&gt;

&lt;p&gt;Instead of taking photos first and building the report somewhere else, both actions can happen in the same app.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned While Building It
&lt;/h2&gt;

&lt;p&gt;The biggest lesson was that a feature is not the same as a complete workflow.&lt;/p&gt;

&lt;p&gt;“Add GPS to a photo” is a feature.&lt;/p&gt;

&lt;p&gt;But the real workflow is closer to this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Arrive at a location.&lt;/li&gt;
&lt;li&gt;Select the correct project.&lt;/li&gt;
&lt;li&gt;Check the GPS signal.&lt;/li&gt;
&lt;li&gt;Take several photos.&lt;/li&gt;
&lt;li&gt;Add notes when needed.&lt;/li&gt;
&lt;li&gt;Keep the photos organized.&lt;/li&gt;
&lt;li&gt;Create a report.&lt;/li&gt;
&lt;li&gt;Send it to someone else.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Thinking about the full workflow changed many of my product decisions.&lt;/p&gt;

&lt;p&gt;I also learned that small utility apps need to be fast.&lt;/p&gt;

&lt;p&gt;Someone standing outside at a job site does not want to go through six setup screens before taking a photo.&lt;/p&gt;

&lt;p&gt;The app should open quickly, show the important information, and let the user continue working.&lt;/p&gt;

&lt;p&gt;That sounds obvious, but it is easy to forget when you are building features from a desk.&lt;/p&gt;

&lt;h2&gt;
  
  
  FieldStamp Is Still a Work in Progress
&lt;/h2&gt;

&lt;p&gt;FieldStamp is currently available on Android and iPhone.&lt;/p&gt;

&lt;p&gt;Android:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://play.google.com/store/apps/details?id=com.fieldstamp.free" rel="noopener noreferrer"&gt;FieldStamp GPS Camera on Google Play&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;iPhone:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://apps.apple.com/us/app/fieldstamp-gps-camera/id6786527961" rel="noopener noreferrer"&gt;FieldStamp GPS Camera on the App Store&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I am still improving the camera experience, project organization, watermark layouts, and PDF reports.&lt;/p&gt;

&lt;p&gt;I am also trying to understand how different people document their field work.&lt;/p&gt;

&lt;p&gt;Some users care most about location accuracy.&lt;/p&gt;

&lt;p&gt;Some care about report creation.&lt;/p&gt;

&lt;p&gt;Others just want a quick way to prove when and where a photo was taken.&lt;/p&gt;

&lt;p&gt;Building this app reminded me that many useful products start from a small, repetitive frustration.&lt;/p&gt;

&lt;p&gt;For me, it was opening my photo gallery and realizing that I could no longer remember the story behind the photos.&lt;/p&gt;

&lt;p&gt;That was the problem I wanted to fix.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>news</category>
      <category>tooling</category>
      <category>react</category>
    </item>
    <item>
      <title>From 0 to 1,000 Scans: The Journey of Building a React Native QR Scanner App</title>
      <dc:creator>Nexa Tech</dc:creator>
      <pubDate>Thu, 16 Oct 2025 14:45:35 +0000</pubDate>
      <link>https://dev.to/tech_nexa_8e64b11ae4f24f9/from-0-to-1000-scans-the-journey-of-building-a-react-native-qr-scanner-app-2b9k</link>
      <guid>https://dev.to/tech_nexa_8e64b11ae4f24f9/from-0-to-1000-scans-the-journey-of-building-a-react-native-qr-scanner-app-2b9k</guid>
      <description>&lt;p&gt;Hello, Dev Community!&lt;/p&gt;

&lt;p&gt;Today, I want to share a story that’s about more than just writing code. It's about solving a problem, relentless learning, and finally, bringing an idea from a local machine to the hands of thousands of users. This is the journey of building ScanQR—my first major mobile application, now live on the Google Play Store.&lt;/p&gt;

&lt;p&gt;This isn't just a "look what I made" post. This is a detailed map of how a modern, high-performance app is born, from the core business logic to the actual code snippets that make it all work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://play.google.com/store/apps/details?id=com.nexa.qr_code" rel="noopener noreferrer"&gt;📲 Download ScanQR for Free on Google Play and See It in Action!&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Part 1: The Problem - Why Build Another QR Scanner?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The market is flooded with scanner apps, but I always felt something was missing. Most apps fell into one of these traps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Slow and Ad-Ridden: The user experience was constantly interrupted.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Too Simple: They just scanned links, with no intelligent features.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Uninspired: Lacked any creative features, like a personalized QR code generator.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wanted an &lt;strong&gt;all-in-one&lt;/strong&gt; tool: lightning-fast, intelligent, secure, and one that empowers user creativity. That was the vision that led to ScanQR.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Part 2: The Heart of ScanQR - A Deep Dive into Core Features and Code&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's dissect the features that make ScanQR stand out and look at the exact React Native code that brought them to life.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The High-Speed, Performance-Optimized Scanner Engine&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the most critical feature. A slow scanner is a useless scanner.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The User Experience: Instantly scan codes, even in low light or when they're slightly damaged.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Inside the Code: I used react-native-vision-camera with its useCodeScanner hook. But I didn't just call the function; I heavily optimized it to prevent re-scans and reduce CPU load.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Here’s the core logic:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ScanScreen.tsx

const lastValueRef = useRef&amp;lt;string&amp;gt;('');
const frameCountRef = useRef(0);
const debounceUntilRef = useRef(0);

const codeScanner = useCodeScanner({
  codeTypes: Platform.OS === 'ios' ? CODE_TYPES_IOS : CODE_TYPES_ANDROID,
  onCodeScanned: codes =&amp;gt; {
    // Only process every Nth frame to save battery
    frameCountRef.current =
      (frameCountRef.current + 1) % SCAN_EVERY_NTH_FRAME;
    if (frameCountRef.current !== 0) return;

    // "Debounce" Logic: Block rapid, repetitive scans
    const now = Date.now();
    if (now &amp;lt; debounceUntilRef.current) return;

    const raw = codes[0]?.value?.trim();
    if (!raw) return;

    // If the code is identical to the last one, wait a bit
    if (raw === lastValueRef.current) {
      debounceUntilRef.current = now + 1000; // Wait 1 second
      return;
    }

    lastValueRef.current = raw;
    debounceUntilRef.current = now + 1200; // Set a cooldown for the new code

    // Parse and display the result
    const parsed = parseLink(raw);
    setResult(parsed);
    bottomSheetRef.current?.expand();
    haptic(); // Trigger haptic feedback
  },
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Dev Analysis:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;frameCountRef&lt;/code&gt;: A simple but effective trick to skip frames. This significantly reduces CPU usage and improves battery life.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;debounceUntilRef&lt;/code&gt; &amp;amp; &lt;code&gt;lastValueRef&lt;/code&gt;: This is a manual debouncing technique. It prevents the app from processing the same QR code dozens of times a second, which would cause the UI to stutter and create a jarring experience.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Smart, Context-Aware Actions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ScanQR doesn't just show you a string of text. It understands the content and suggests the appropriate action.&lt;/p&gt;

&lt;p&gt;The User Experience: Scan a URL, get an "Open" button. Scan a phone number, get a "Call" button. Scan Wi-Fi credentials, get a "Copy Password" action.&lt;/p&gt;

&lt;p&gt;Inside the Code: This logic lives in the primaryAction function. It's a large switch statement that operates on the result from my custom parseLink function.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here's a snippet:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ScanScreen.tsx

const primaryAction = async () =&amp;gt; {
  if (!result) return;
  switch (result.kind) {
    case 'URL':
      await Linking.openURL(result.href);
      break;
    case 'PHONE':
      await Linking.openURL(`tel:${result.number}`);
      break;
    case 'EMAIL': {
      // ... logic to build email query params ...
      await Linking.openURL(`mailto:${result.to ?? ''}?${qp.toString()}`);
      break;
    }
    case 'WIFI':
      Clipboard.setString(
        `WIFI:T:${result.encryption};S:${result.ssid};P:${result.password};;`,
      );
      Toast.show({
        type: 'success',
        text1: t('message.copied_to_clipboard'),
      });
      break;
    // ... many more cases for vCard, SMS, Geo, Crypto ...
    default:
      if ('text' in result) {
        Clipboard.setString(result.text);
        Toast.show({ /* ... */ });
      }
      break;
  }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Dev Analysis&lt;/strong&gt;: Separating the parsing logic (&lt;code&gt;parseLink&lt;/code&gt;) from the action logic (&lt;code&gt;primaryAction&lt;/code&gt;) keeps the code clean and maintainable. Using React Native's built-in &lt;code&gt;Linking&lt;/code&gt; and &lt;code&gt;Clipboard&lt;/code&gt; APIs allows for deep integration with the operating system.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;3. Scan QR Codes from the Image Gallery&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is a convenient feature that many users requested.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The User Experience&lt;/strong&gt;: Pick a photo containing a QR code from your gallery, and ScanQR will instantly recognize it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Inside the Code&lt;/strong&gt;: I combined two powerful libraries: &lt;code&gt;react-native-image-crop-picker&lt;/code&gt; for selecting the image and &lt;code&gt;@react-native-ml-kit/barcode-scanning&lt;/code&gt; for analyzing the static image.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The implementation is quite straightforward:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ScanScreen.tsx
import ImageCropPicker from 'react-native-image-crop-picker';
import BarcodeScanning from '@react-native-ml-kit/barcode-scanning';

const onImagePicker = () =&amp;gt; {
  ImageCropPicker.openPicker({ mediaType: 'photo' })
    .then(async image =&amp;gt; {
      if (!image?.sourceURL &amp;amp;&amp;amp; !image?.path) return;
      try {
        const uri = image.sourceURL ?? image.path;
        // Use ML Kit to scan the code from the image's URI
        const barcodes = await BarcodeScanning.scan(uri);
        const raw = barcodes?.[0]?.value?.trim();

        if (!raw) throw new Error('No barcode found');

        // Reuse the same parsing and display logic
        const parsed = parseLink(raw);
        setResult(parsed);
        bottomSheetRef.current?.expand();
        haptic();
      } catch {
        Toast.show({ type: 'error', text1: t('message.invalid_qr_code') });
      }
    })
    .catch(err =&amp;gt; { console.log('Image picker error', err); });
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Dev Analysis&lt;/strong&gt;: The beauty here is reusability. Once I get the raw data from the image, I pass it through the exact same processing flow (&lt;code&gt;parseLink&lt;/code&gt;, &lt;code&gt;setResult&lt;/code&gt;, expand bottom sheet) as a camera scan. This is efficient and reduces code duplication.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Part 3: The Final Stretch - Publishing to the Google Play Store&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Finishing the code is only half the battle. The publishing process is a whole other challenge:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Creating the App Bundle (.aab)&lt;/strong&gt;: Learning the process of signing and generating a production-ready build for Google Play.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Crafting the Store Presence&lt;/strong&gt;: Designing an icon, taking screenshots, and writing compelling, SEO-friendly descriptions for the app page. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Privacy Policy&lt;/strong&gt;: This is a must. I created a simple, clear privacy policy on GitHub Pages, committing to user privacy. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Anxious Wait&lt;/strong&gt;: Submitting the app and waiting for Google's review. The feeling of seeing that "Your app is now live" email is indescribable!&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;strong&gt;Conclusion and Your Invitation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The journey of building ScanQR has taught me so much about React Native, performance optimization, and the full product development lifecycle.&lt;/p&gt;

&lt;p&gt;It was built with a developer's passion but designed for everyone. If you're looking for a powerful, privacy-respecting, and completely free QR code tool, please give ScanQR a try.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Download the App&lt;/strong&gt;: &lt;a href="https://play.google.com/store/apps/details?id=com.nexa.qr_code" rel="noopener noreferrer"&gt;Link&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo1xoyet35dfxgopxsyvd.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%2Fo1xoyet35dfxgopxsyvd.png" alt="Link Application" width="800" height="1037"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Put It to the Test&lt;/strong&gt;: Scan everything, generate your own codes, and experience the difference.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Leave Feedback&lt;/strong&gt;: Your reviews and suggestions are invaluable and will help shape the future of the app.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Thank you for reading my story. I hope it inspires anyone out there with a project idea of their own!&lt;/p&gt;

&lt;p&gt;A question for my fellow devs: What was the biggest technical hurdle you've ever faced in a personal project? Share it in the comments below!&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>javascript</category>
      <category>typescript</category>
      <category>mobile</category>
    </item>
    <item>
      <title>A Personal Guide to Setting Up React Native on Mac: My Journey</title>
      <dc:creator>Nexa Tech</dc:creator>
      <pubDate>Sun, 12 Oct 2025 15:29:22 +0000</pubDate>
      <link>https://dev.to/tech_nexa_8e64b11ae4f24f9/a-personal-guide-to-setting-up-react-native-on-mac-my-journey-2o62</link>
      <guid>https://dev.to/tech_nexa_8e64b11ae4f24f9/a-personal-guide-to-setting-up-react-native-on-mac-my-journey-2o62</guid>
      <description>&lt;p&gt;Hello, future teammate!&lt;/p&gt;

&lt;p&gt;If you're reading this, I bet you're just as excited as I was to start building amazing mobile apps with React Native. It's an awesome choice! I've been exactly where you are now: thrilled, but also a little overwhelmed by the sheer number of tools you need to install.&lt;/p&gt;

&lt;p&gt;This isn't another dry, technical document. This is my journal, my "lessons learned the hard way," shared with the hope of making your path a little smoother. Let's get started together!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Gearing Up with the Right Tools&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Just like a skilled carpenter needs a good set of chisels and a sturdy workbench, we need a solid foundation of software tools.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Homebrew - Your Ultimate Butler:&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Think of Homebrew as your personal tech butler. Instead of hunting for download links on countless websites, you just type a command, and Homebrew fetches and installs everything for you. If you haven't met it yet, open your Terminal and summon it with this spell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Official Docs:&lt;/strong&gt; &lt;a href="https://brew.sh" rel="noopener noreferrer"&gt;Homebrew Website&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Node.js &amp;amp; Watchman - The Heart and the Pulse:&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Node.js&lt;/strong&gt; is the 'heart' that pumps life into your JavaScript code. It's the environment where everything runs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Watchman&lt;/strong&gt; is the 'pulse.' It's a little tool from Facebook that keeps an eye on your files. The moment you save a change, Watchman sees it and tells your app to reload instantly. This is the magic behind "Fast Refresh."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's get them both with our new butler, Homebrew:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew install node
brew install watchman
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Official Docs:&lt;/strong&gt; &lt;a href="https://nodejs.org/" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; &amp;amp; &lt;a href="https://facebook.github.io/watchman/" rel="noopener noreferrer"&gt;Watchman&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 2: The iOS Adventure&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To build apps for iPhones and iPads, we need to enter Apple's world. Xcode is the key.&lt;/p&gt;

&lt;p&gt;1.&lt;strong&gt;Install Xcode:&lt;/strong&gt; The best place to get this is the Mac App Store. A friendly warning: it's a huge file. This is a perfect time to go make a fresh cup of coffee (or two). It'll take a while.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Official Docs:&lt;/strong&gt; &lt;a href="https://developer.apple.com/xcode/" rel="noopener noreferrer"&gt;Xcode on the Apple Developer Website&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2.&lt;strong&gt;Set Up Command Line Tools:&lt;/strong&gt; Once Xcode is installed, open it up. Go to Xcode &amp;gt; Settings &amp;gt; Locations. In the dropdown for "Command Line Tools," make sure a version is selected. This is like telling your Mac, "Hey, I've got all these cool new tools from Xcode, and you're allowed to use them from the Terminal."&lt;/p&gt;

&lt;p&gt;3.&lt;strong&gt;CocoaPods - The Librarian:&lt;/strong&gt;&lt;br&gt;
Almost every iOS project uses extra bits of code from other developers, called libraries. CocoaPods is the friendly librarian that manages all of these for you. Let's install it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo gem install cocoapods
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Official Docs:&lt;/strong&gt; &lt;a href="https://cocoapods.org/" rel="noopener noreferrer"&gt;CocoaPods Website&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 3: The Android Quest&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Android setup can feel like a side quest with a few more steps, but trust me, the feeling of seeing your app run on both platforms is worth it.&lt;/p&gt;

&lt;p&gt;1.&lt;strong&gt;Java Development Kit (JDK) - The Lingua Franca:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Android's native language is Java/Kotlin, so we need a JDK to help React Native talk to it. Let's have Homebrew handle this for us:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew install --cask zulu@17
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the JDK installation, add or update your &lt;code&gt;JAVA_HOME&lt;/code&gt; environment variable in &lt;code&gt;~/.zshrc&lt;/code&gt; (or in &lt;code&gt;~/.bash_profile&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;If you used above steps, JDK will likely be located at &lt;code&gt;/Library/Java/JavaVirtualMachines/zulu-17.jdk/Contents/Home&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export JAVA_HOME=/Library/Java/JavaVirtualMachines/zulu-17.jdk/Contents/Home
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2.&lt;strong&gt;Android Studio - The Command Center:&lt;/strong&gt;&lt;br&gt;
Download and install Android Studio from Google's website. During the setup, choose "Custom" installation and make sure these are checked:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Android SDK&lt;/li&gt;
&lt;li&gt;Android SDK Platform&lt;/li&gt;
&lt;li&gt;Android Virtual Device (this lets you create phone emulators)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Official Docs:&lt;/strong&gt; &lt;a href="https://developer.android.com/studio" rel="noopener noreferrer"&gt;Android Studio Website&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;3.&lt;strong&gt;Teaching Your Terminal the Way - Environment Variables:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the step where many people get stuck, but it's not as scary as it looks. We just need to leave a note for our Terminal telling it where to find all the Android tools.&lt;/p&gt;

&lt;p&gt;Open your &lt;strong&gt;~/.zshrc&lt;/strong&gt; file (it's a hidden file in your home directory) and add these lines at the bottom:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export ANDROID_HOME=$HOME/Library/Android/sdk
export PATH=$PATH:$ANDROID_HOME/emulator
export PATH=$PATH:$ANDROID_HOME/platform-tools
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After you save it, close and reopen your Terminal. This simple step makes sure that when you type a command like &lt;code&gt;emulator&lt;/code&gt;, your computer knows exactly where to look.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Learn more:&lt;/strong&gt; &lt;a href="https://reactnative.dev/docs/set-up-your-environment" rel="noopener noreferrer"&gt;React Native Docs on Environment Variables&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 4: The "It's Alive!" Magic Moment&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Alright, the stage is set. It's time to bring your first creation to life.&lt;/p&gt;

&lt;p&gt;1.&lt;strong&gt;Create Your Project:&lt;/strong&gt;&lt;br&gt;
In your Terminal, run this command. &lt;code&gt;MyFirstApp&lt;/code&gt; can be whatever you want to name your project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx @react-native-community/cli@latest init MyFirstApp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2.&lt;strong&gt;Step Inside:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd MyFirstApp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;2.1.&lt;strong&gt;Run on IOS:&lt;/strong&gt;&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run ios
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;This will automatically boot up the iOS Simulator and you should see your app pop up. It's a magical feeling the first time!&lt;/p&gt;

&lt;p&gt;2.2.&lt;strong&gt;Run on Android:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Run on Android:&lt;br&gt;
First, open Android Studio and start an emulator from the AVD Manager. Then, in your terminal, run:&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run android
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;And voilà! The same app, running on a completely different system.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Don't Panic! We've All Been There (Troubleshooting)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you hit a roadblock, take a deep breath. Every single developer has faced these errors. You're not alone. Here are some common hurdles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Problem:&lt;/strong&gt; &lt;code&gt;pod install&lt;/code&gt; fails on your shiny new M1/M2/M3 Mac.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Story:&lt;/strong&gt; Your Mac's chip speaks a new language (ARM), but some old tools still speak the old one (x86). Sometimes they get lost in translation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Fix:&lt;/strong&gt; Tell your Mac to use its built-in translator, Rosetta.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd ios
arch -x86_64 pod install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Problem:&lt;/strong&gt; Android build complains it "can't find SDK location."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Story:&lt;/strong&gt; The message you left in &lt;code&gt;~/.zshrc&lt;/code&gt; got lost, or you forgot to restart the Terminal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Fix:&lt;/strong&gt; Double-check the path in &lt;code&gt;~/.zshrc&lt;/code&gt;. If it's still not working, you can leave a more direct note inside the project. Create a file at &lt;code&gt;android/local.properties&lt;/code&gt; and put this inside (remember to change &lt;code&gt;YOUR_USERNAME&lt;/code&gt;!):
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sdk.dir = /Users/YOUR_USERNAME/Library/Android/sdk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Your Journey Begins Now&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And there you have it! You've successfully built your launchpad. The road ahead is filled with exciting challenges, learning, and the incredible joy of creating something from scratch. For your next steps, the official React Native documentation is your best friend.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dive Deeper:&lt;/strong&gt; &lt;a href="https://reactnative.dev/docs/getting-started" rel="noopener noreferrer"&gt;React Native Official Documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Welcome to the club. Happy coding!&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>programming</category>
    </item>
    <item>
      <title>A Personal Guide to Setting Up React Native on Mac: My Journey</title>
      <dc:creator>Nexa Tech</dc:creator>
      <pubDate>Sun, 12 Oct 2025 15:29:22 +0000</pubDate>
      <link>https://dev.to/tech_nexa_8e64b11ae4f24f9/a-personal-guide-to-setting-up-react-native-on-mac-my-journey-30pl</link>
      <guid>https://dev.to/tech_nexa_8e64b11ae4f24f9/a-personal-guide-to-setting-up-react-native-on-mac-my-journey-30pl</guid>
      <description>&lt;p&gt;Hello, future teammate!&lt;/p&gt;

&lt;p&gt;If you're reading this, I bet you're just as excited as I was to start building amazing mobile apps with React Native. It's an awesome choice! I've been exactly where you are now: thrilled, but also a little overwhelmed by the sheer number of tools you need to install.&lt;/p&gt;

&lt;p&gt;This isn't another dry, technical document. This is my journal, my "lessons learned the hard way," shared with the hope of making your path a little smoother. Let's get started together!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Gearing Up with the Right Tools&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Just like a skilled carpenter needs a good set of chisels and a sturdy workbench, we need a solid foundation of software tools.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Homebrew - Your Ultimate Butler:&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Think of Homebrew as your personal tech butler. Instead of hunting for download links on countless websites, you just type a command, and Homebrew fetches and installs everything for you. If you haven't met it yet, open your Terminal and summon it with this spell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Official Docs:&lt;/strong&gt; &lt;a href="https://brew.sh" rel="noopener noreferrer"&gt;Homebrew Website&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Node.js &amp;amp; Watchman - The Heart and the Pulse:&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Node.js&lt;/strong&gt; is the 'heart' that pumps life into your JavaScript code. It's the environment where everything runs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Watchman&lt;/strong&gt; is the 'pulse.' It's a little tool from Facebook that keeps an eye on your files. The moment you save a change, Watchman sees it and tells your app to reload instantly. This is the magic behind "Fast Refresh."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's get them both with our new butler, Homebrew:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew install node
brew install watchman
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Official Docs:&lt;/strong&gt; &lt;a href="https://nodejs.org/" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; &amp;amp; &lt;a href="https://facebook.github.io/watchman/" rel="noopener noreferrer"&gt;Watchman&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 2: The iOS Adventure&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To build apps for iPhones and iPads, we need to enter Apple's world. Xcode is the key.&lt;/p&gt;

&lt;p&gt;1.&lt;strong&gt;Install Xcode:&lt;/strong&gt; The best place to get this is the Mac App Store. A friendly warning: it's a huge file. This is a perfect time to go make a fresh cup of coffee (or two). It'll take a while.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Official Docs:&lt;/strong&gt; &lt;a href="https://developer.apple.com/xcode/" rel="noopener noreferrer"&gt;Xcode on the Apple Developer Website&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2.&lt;strong&gt;Set Up Command Line Tools:&lt;/strong&gt; Once Xcode is installed, open it up. Go to Xcode &amp;gt; Settings &amp;gt; Locations. In the dropdown for "Command Line Tools," make sure a version is selected. This is like telling your Mac, "Hey, I've got all these cool new tools from Xcode, and you're allowed to use them from the Terminal."&lt;/p&gt;

&lt;p&gt;3.&lt;strong&gt;CocoaPods - The Librarian:&lt;/strong&gt;&lt;br&gt;
Almost every iOS project uses extra bits of code from other developers, called libraries. CocoaPods is the friendly librarian that manages all of these for you. Let's install it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo gem install cocoapods
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Official Docs:&lt;/strong&gt; &lt;a href="https://cocoapods.org/" rel="noopener noreferrer"&gt;CocoaPods Website&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 3: The Android Quest&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Android setup can feel like a side quest with a few more steps, but trust me, the feeling of seeing your app run on both platforms is worth it.&lt;/p&gt;

&lt;p&gt;1.&lt;strong&gt;Java Development Kit (JDK) - The Lingua Franca:&lt;/strong&gt;&lt;br&gt;
Android's native language is Java/Kotlin, so we need a JDK to help React Native talk to it. Let's have Homebrew handle this for us:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew install --cask zulu@17
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the JDK installation, add or update your &lt;code&gt;JAVA_HOME&lt;/code&gt; environment variable in &lt;code&gt;~/.zshrc&lt;/code&gt; (or in &lt;code&gt;~/.bash_profile&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;If you used above steps, JDK will likely be located at &lt;code&gt;/Library/Java/JavaVirtualMachines/zulu-17.jdk/Contents/Home&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export JAVA_HOME=/Library/Java/JavaVirtualMachines/zulu-17.jdk/Contents/Home
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2.&lt;strong&gt;Android Studio - The Command Center:&lt;/strong&gt;&lt;br&gt;
Download and install Android Studio from Google's website. During the setup, choose "Custom" installation and make sure these are checked:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Android SDK&lt;/li&gt;
&lt;li&gt;Android SDK Platform&lt;/li&gt;
&lt;li&gt;Android Virtual Device (this lets you create phone emulators)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Official Docs:&lt;/strong&gt; &lt;a href="https://developer.android.com/studio" rel="noopener noreferrer"&gt;Android Studio Website&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;3.&lt;strong&gt;Teaching Your Terminal the Way - Environment Variables: **&lt;br&gt;
This is the step where many people get stuck, but it's not as scary as it looks. We just need to leave a note for our Terminal telling it where to find all the Android tools.&lt;br&gt;
Open your **~/.zshrc&lt;/strong&gt; file (it's a hidden file in your home directory) and add these lines at the bottom:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export ANDROID_HOME=$HOME/Library/Android/sdk
export PATH=$PATH:$ANDROID_HOME/emulator
export PATH=$PATH:$ANDROID_HOME/platform-tools
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After you save it, close and reopen your Terminal. This simple step makes sure that when you type a command like &lt;code&gt;emulator&lt;/code&gt;, your computer knows exactly where to look.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Learn more:&lt;/strong&gt; &lt;a href="https://reactnative.dev/docs/set-up-your-environment" rel="noopener noreferrer"&gt;React Native Docs on Environment Variables&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 4: The "It's Alive!" Magic Moment&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Alright, the stage is set. It's time to bring your first creation to life.&lt;/p&gt;

&lt;p&gt;1.&lt;strong&gt;Create Your Project:&lt;/strong&gt;&lt;br&gt;
In your Terminal, run this command. &lt;code&gt;MyFirstApp&lt;/code&gt; can be whatever you want to name your project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx @react-native-community/cli@latest init MyFirstApp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2.&lt;strong&gt;Step Inside:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd MyFirstApp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;2.1.&lt;strong&gt;Run on IOS:&lt;/strong&gt;&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run ios
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;This will automatically boot up the iOS Simulator and you should see your app pop up. It's a magical feeling the first time!&lt;/p&gt;

&lt;p&gt;2.2*&lt;em&gt;Run on Android:&lt;/em&gt;*&lt;br&gt;
Run on Android:&lt;br&gt;
First, open Android Studio and start an emulator from the AVD Manager. Then, in your terminal, run:&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run android
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;And voilà! The same app, running on a completely different system.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Don't Panic! We've All Been There (Troubleshooting)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you hit a roadblock, take a deep breath. Every single developer has faced these errors. You're not alone. Here are some common hurdles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Problem:&lt;/strong&gt; &lt;code&gt;pod install&lt;/code&gt; fails on your shiny new M1/M2/M3 Mac.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Story:&lt;/strong&gt; Your Mac's chip speaks a new language (ARM), but some old tools still speak the old one (x86). Sometimes they get lost in translation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Fix:&lt;/strong&gt; Tell your Mac to use its built-in translator, Rosetta.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd ios
arch -x86_64 pod install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Problem:&lt;/strong&gt; Android build complains it "can't find SDK location."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Story:&lt;/strong&gt; The message you left in &lt;code&gt;~/.zshrc&lt;/code&gt; got lost, or you forgot to restart the Terminal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Fix:&lt;/strong&gt; Double-check the path in &lt;code&gt;~/.zshrc&lt;/code&gt;. If it's still not working, you can leave a more direct note inside the project. Create a file at &lt;code&gt;android/local.properties&lt;/code&gt; and put this inside (remember to change &lt;code&gt;YOUR_USERNAME&lt;/code&gt;!):
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sdk.dir = /Users/YOUR_USERNAME/Library/Android/sdk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Your Journey Begins Now&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And there you have it! You've successfully built your launchpad. The road ahead is filled with exciting challenges, learning, and the incredible joy of creating something from scratch. For your next steps, the official React Native documentation is your best friend.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dive Deeper:&lt;/strong&gt; &lt;a href="https://reactnative.dev/docs/getting-started" rel="noopener noreferrer"&gt;React Native Official Documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Welcome to the club. Happy coding!&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
