I shipped my first iOS app in 30 days for $300. Here's the build log. A developer built and shipped Chista, an iOS app that auto-imports and classifies screenshots using AI, in 30 days for about $300. The app uses a SwiftUI frontend with a Python backend on FastAPI, Supabase for database and auth, and OpenAI GPT-4o for categorization. The developer documented a critical bug in Apple's in-app purchase verification library where an empty root_certificates list causes all JWS verifications to fail, requiring manual inclusion of Apple's root CA certificates. I take a lot of screenshots. The article I'll read later. The recipe I'll cook on Sunday. The movie name from someone's Instagram story. A job post, a product, a tender. Most of them die in my camera roll. So I built Chista — an iOS app that auto-imports every screenshot, classifies it with AI Article, Product, Event, Reference, Media , and surfaces a one-tap action: It shipped on the App Store thirty days after I started, for about $300 in total cost. The interesting part wasn't the app. It was what the build revealed. Chista is a native iOS app + Python backend. PHPhotoLibraryChangeObserver , scoped to PHAssetMediaSubtype.photoScreenshot so it literally can't see your other photos . CategorizationResult JSON: category, subtype, title, suggested action, extracted data price, deadline, URL, etc. .That's the whole thing. The "magic moment" is just: you screenshot something, switch to Chista a few seconds later, it's already sorted with a contextual action button. | Layer | Tool | Why | |---|---|---| | iOS app | Swift 5.10, SwiftUI, StoreKit 2 | iOS 17+, modern surface | | Backend | FastAPI on Railway | One-file ergonomics, fast cold starts | | Database + Auth | Supabase | Postgres + JWT auth out of the box | | AI | OpenAI GPT-4o Pro , gpt-4o-mini Free | Tier-routed at categorization time | | Push | APNs via aioapns | Direct, no Firebase middleman | | Subscriptions | StoreKit 2 + app-store-server-library | Server-side JWS verification | | Affiliate routing | Custom matrix in Supabase tables | Amazon Associates wired, more pending | | Hosting web | Cloudflare Pages | Free, fast, never goes down | No frameworks I wouldn't reach for again. | Line item | Cost | |---|---| | Apple Developer Account | $99/yr | | Domain chista.app | $10/yr | | OpenAI API credits | $100 one-off prepaid | | Cloudflare web + DNS | free | | Railway backend | $5/mo trial credit, then hobby tier | | Supabase | free tier | | Everything else | mostly free tiers | Total to ship v1: about $300. Apple App Review's reviewer kept hitting INVALID CERTIFICATE when validating in-app purchase JWS payloads on our backend. We chased it through four library versions: app-store-server-library==1.8.0 → INVALID CERTIFICATE app-store-server-library==1.9.0 → INVALID CERTIFICATE app-store-server-library==2.0.0 → INVALID CERTIFICATE app-store-server-library==3.1.2 → INVALID CERTIFICATE I enabled online cert checks. I bundled app apple id . I made the verifier environment switch independently from APNs. Nothing worked. The actual problem: the library has a constructor like this: SignedDataVerifier root certificates= , ← THIS enable online checks=True, environment=Environment.SANDBOX, bundle id="com.chista.app", app apple id=6771318695, I assumed root certificates= meant "use Apple's bundled roots." It doesn't. The library ships with zero root CAs. An empty list means "trust nothing." Every Apple-signed JWS fails because the cert chain has no trust anchor. The fix: download Apple's Root CA G2 and G3 directly from apple.com/certificateauthority https://www.apple.com/certificateauthority/ , check them into the repo, and pass the bytes: cert dir = Path file .parent.parent / "data" / "apple certs" root certs = cert dir / "AppleRootCA-G3.cer" .read bytes , cert dir / "AppleRootCA-G2.cer" .read bytes , SignedDataVerifier root certificates=root certs, enable online checks=True, environment=env, bundle id=bundle id, app apple id=app apple id, One line of additional config, ~2KB committed to the repo, problem solved. If you're integrating Apple's library and hitting this: the empty default is the bug, not your environment. The official docs imply bundled roots; the code does not. I built Chista with heavy AI assistance — drafting Swift views, generating prompt templates, debugging gnarly things like the cert chain above. It would be dishonest to tell this story without saying so. What that actually changed: That last point is where I think the whole industry is heading. A decade ago, the hard part was writing software. Today, the hard part is deciding what should exist. The cost of creation is collapsing. The time from idea to first version is shrinking. The number of people who can build is exploding. None of this is news on dev.to — but watching it happen in real time on my own project felt different than reading about it. Software is starting to behave like content. The bottleneck is moving from can you build this to should this exist, and is it actually better than what's already there. That's a great problem to have. AMAZON ASSOCIATES US=... style env vars for affiliate keys. Six countries in, I moved them all to a affiliate keys Postgres table. Should have done it on day one.Chista is on the App Store https://apps.apple.com/app/chista-screenshot-inbox/id6771318695 . Free tier covers 30 screenshots/month. Pro is $4.99/mo with a 14-day trial. I'd love to hear what's in your camera roll graveyard. This post mirrors a version on chista.app. The canonical link is set so dev.to credits the source domain for SEO.