<?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: All Smart Accessories</title>
    <description>The latest articles on DEV Community by All Smart Accessories (@allsmartaccessories).</description>
    <link>https://dev.to/allsmartaccessories</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%2F4180234%2F2d19360c-12e7-4945-9958-d7263cc5dfbf.png</url>
      <title>DEV Community: All Smart Accessories</title>
      <link>https://dev.to/allsmartaccessories</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/allsmartaccessories"/>
    <language>en</language>
    <item>
      <title>I built a case compatibility checker covering 67 phone models (and open-sourced it)</title>
      <dc:creator>All Smart Accessories</dc:creator>
      <pubDate>Mon, 12 Oct 2026 19:20:25 +0000</pubDate>
      <link>https://dev.to/allsmartaccessories/i-built-a-case-compatibility-checker-covering-67-phone-models-and-open-sourced-it-cid</link>
      <guid>https://dev.to/allsmartaccessories/i-built-a-case-compatibility-checker-covering-67-phone-models-and-open-sourced-it-cid</guid>
      <description>&lt;h1&gt;
  
  
  Why I built this
&lt;/h1&gt;

&lt;p&gt;I run a phone accessories blog. Every week the same question shows up in comments and support emails:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I have a Galaxy S24 Ultra — which case fits it?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I was answering it by hand, then linking to a buying guide, then hoping the reader found the right section. That's a bad experience, so I built a tool that just answers it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://allsmartaccessories.com/tools/case-checker/" rel="noopener noreferrer"&gt;&lt;strong&gt;Case Compatibility Checker&lt;/strong&gt;&lt;/a&gt; — pick a brand, pick a model, and you get the compatible case types plus screen protector info.&lt;/p&gt;

&lt;p&gt;67 models are covered: Apple iPhone 6 through 17, Samsung Galaxy S7 through S24 Ultra, the Note line up to the Note 20 Ultra, plus Pixel, OnePlus, HTC, LG, Motorola, Xiaomi, ZTE and Nokia.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two front-ends, one dataset
&lt;/h2&gt;

&lt;p&gt;The interesting design problem: I wanted a web tool &lt;em&gt;and&lt;/em&gt; a CLI, without duplicating the data or the logic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;casefinder/
  casefinder/
    __init__.py
    __main__.py
    cli.py        # argparse front-end
    data.py       # dataset loading
    data/
      phones.json # the actual dataset
  index.html      # static web front-end
  app.js          # fetches the same phones.json
  styles.css
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both front-ends read &lt;strong&gt;the same &lt;code&gt;phones.json&lt;/code&gt;&lt;/strong&gt;. The CLI runs offline for shell scripting; the web app fetches the file over HTTP. A test asserts they're identical, so they can't silently drift apart.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;casefinder
casefinder check &lt;span class="s2"&gt;"Galaxy S24 Ultra"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;        &lt;span class="s"&gt;Galaxy S24 Ultra&lt;/span&gt;
&lt;span class="na"&gt;Brand&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;        &lt;span class="s"&gt;Samsung&lt;/span&gt;
&lt;span class="na"&gt;Released&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;     &lt;span class="m"&gt;2024&lt;/span&gt;
&lt;span class="na"&gt;MagSafe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;      &lt;span class="s"&gt;no&lt;/span&gt;

&lt;span class="na"&gt;Case types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;rugged&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;clear&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;slim&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;wallet&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;battery&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;kickstand&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;waterproof&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;stylish&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;bumper&lt;/span&gt;

&lt;span class="na"&gt;Screen protector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tempered glass, film&lt;/span&gt;
&lt;span class="na"&gt;Guide&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://allsmartaccessories.com/top-10-best-galaxy-s24-ultra-cases/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Where the data came from
&lt;/h2&gt;

&lt;p&gt;This is the part I care most about. &lt;strong&gt;I did not invent any of it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The 67 models and their compatible case types were derived from my existing published guides — each entry links back to the real post it came from. Every one of those 67 URLs was verified with a live HTTP request before it went into the dataset.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Galaxy S24 Ultra"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"brand"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Samsung"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"released"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"magsafe"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"caseTypes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"rugged"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"clear"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"slim"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"screenProtector"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"tempered glass"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"film"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"postUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://allsmartaccessories.com/top-10-best-galaxy-s24-ultra-cases/"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;dimensionsMm&lt;/code&gt; is deliberately &lt;code&gt;null&lt;/code&gt; rather than a guessed number. The UI hides that row instead of showing something made up.&lt;/p&gt;

&lt;h2&gt;
  
  
  TDD, in the order I actually built it
&lt;/h2&gt;

&lt;p&gt;I wrote each test file first, watched it fail, then implemented. Not because it's fashionable — because I kept changing my mind about the schema and the tests caught me.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;test_schema.py&lt;/code&gt; — the shape of &lt;code&gt;phones.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;test_cli.py&lt;/code&gt; — &lt;code&gt;brands&lt;/code&gt;, &lt;code&gt;models&lt;/code&gt;, &lt;code&gt;check&lt;/code&gt; subcommands&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;test_packaging.py&lt;/code&gt; — the wheel builds and the entry point works&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;test_webapp.py&lt;/code&gt; — the HTML renders, and &lt;strong&gt;no &lt;code&gt;innerHTML&lt;/code&gt;&lt;/strong&gt; anywhere&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;test_sync.py&lt;/code&gt; — CLI data and web data are the same file&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;22 tests, and a GitHub Action runs them on every push.&lt;/p&gt;

&lt;h3&gt;
  
  
  The one that caught me
&lt;/h3&gt;

&lt;p&gt;My XSS test grepped &lt;code&gt;app.js&lt;/code&gt; for &lt;code&gt;innerHTML&lt;/code&gt;. It failed — on my &lt;em&gt;own comment&lt;/em&gt; where I'd written "we never use innerHTML here." That was a genuinely useful failure, because it proved the gate was real and not decoration. I fixed the test to strip comments first.&lt;/p&gt;

&lt;h2&gt;
  
  
  The SEO angle
&lt;/h2&gt;

&lt;p&gt;The web page is a single static HTML file with 67 real &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; links to the guides. No framework, no build step, ~49 KB total, zero external requests.&lt;/p&gt;

&lt;p&gt;Two reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Crawlable.&lt;/strong&gt; Google sees actual text and 67 internal links. A JS-only widget would show up as an empty page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fast.&lt;/strong&gt; No render-blocking fonts, no CDN, no framework hydration.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Plus &lt;code&gt;WebApplication&lt;/code&gt; JSON-LD and OG tags, matching what the rest of my WordPress site already emits via Yoast.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd do differently
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;I should have written the schema first.&lt;/strong&gt; I rewrote &lt;code&gt;phones.json&lt;/code&gt; twice because I kept adding fields mid-build.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;pip install&lt;/code&gt; from a relative path is a trap&lt;/strong&gt; in tests. Had to be explicit about it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify URLs earlier.&lt;/strong&gt; I discovered the bad Note 20 Ultra link late and had to re-check all 67 anyway.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Web: &lt;a href="https://allsmartaccessories.com/tools/case-checker/" rel="noopener noreferrer"&gt;Case Compatibility Checker&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;CLI: &lt;code&gt;pip install casefinder&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Code: &lt;a href="https://github.com/allsmartaccessories/case-compatibility-checker" rel="noopener noreferrer"&gt;github.com/allsmartaccessories/case-compatibility-checker&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's MIT licensed. If you maintain a product database and want the same "two front-ends, one dataset" structure, the test that keeps them in sync is the piece worth stealing.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you found this useful, the &lt;a href="https://allsmartaccessories.com/tools/case-checker/" rel="noopener noreferrer"&gt;Case Compatibility Checker&lt;/a&gt; is free and covers 67 models.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>webdev</category>
      <category>opensource</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
