How I Built Spree — An AI Wishlist App with Virtual Try-On
How I Built Spree — An AI Wishlist App with Virtual Try-On
Quick answer: Spree is a free iOS wishlist app that lets you paste any product URL from any online store and automatically imports the title, price, and image. It includes swipe-to-rank for decision-making and an AI virtual try-on feature for clothing. You can find it at [tryspree.app](https://tryspree.app) or search "Spree Wishlist" on the App Store.
The real problem with online wishlists
My wishlist was a mess of screenshots inside a Notes app. Hundreds of them. No prices, no links, no order. Just a graveyard of things I'd once wanted and completely forgotten about.
Talk to almost anyone who shops online and they'll describe the same thing. Screenshots in Notes. Maybe a browser bookmarks folder that spirals into chaos. Amazon Wishlist if everything you want happens to be on Amazon, which it rarely is.
That gap bothered me long enough that I eventually just built something.
The existing tools solve the wrong problem. Amazon Wishlist works great — inside Amazon. The moment you find something on ASOS, Zara, or a small brand's Shopify store, you're back to screenshots.
Pinterest fills a different role. It's mood board energy. Discovery. You're not going to Pinterest to decide between two specific jackets you're ready to buy. That's fine. But inspiration and purchase decisions are different mental modes.
What I actually wanted was something that could hold items from any store, help me rank them so I could figure out what I actually wanted most, and — this is the part that kept coming back to me — show me what clothes would look like on me before I committed.
Why I chose Swift/SwiftUI
I'll be direct: this app has a heavy gesture layer. The swipe-to-rank mechanic is the core interaction. Cards need to feel physical. The drag resistance, the rotation, the snap-back animation — all of it has to feel instant.
I prototyped the swipe UX in React Native first. It worked. It was also obviously not good enough. There was a subtle latency in the gesture recognition that made cards feel slightly detached from your finger. On paper the frame rate was fine. In practice it felt off.
SwiftUI with native gesture recognizers removed that entirely. The cards track your finger at 120Hz on supported hardware. That's the difference between "this feels like a real app" and "this feels like a web wrapper."
For a reading app or a form-heavy tool, React Native is a perfectly reasonable choice. For an app where the primary interaction is physical, native wins.
How product import actually works
The core promise of Spree is simple: paste a URL, get a clean product card. Making that actually work across thousands of different store layouts is the hard part.
The scraping layer
Most e-commerce sites include Open Graph meta tags. og:title, og:image, og:price:amount. These were designed for social sharing previews, but they're exactly what I needed. For well-structured stores — Shopify, major retailers — Open Graph alone gets me 80% of the way there.
The remaining 20% is messier. Sites that don't populate OG tags need structured data parsing (JSON-LD product schema) as a fallback. And then there's a small category of sites that require deeper parsing of the HTML itself.
I wrote the import service to cascade through those layers in order. Try OG tags first. Fall back to JSON-LD. Fall back to heuristic HTML parsing. It's not perfect — nothing scraping arbitrary HTML will be — but the success rate is high enough that the core experience works.
Why not a third-party API?
I looked at services that offer structured product data extraction. Some of them are good. All of them add cost per request, which is a problem for a free tier app. More importantly, they add latency I couldn't control. The import had to feel fast or the UX would break. I wanted the card to appear in under two seconds. Building the parser myself gave me that control.
The swipe-to-rank mechanic
Most wishlists are just lists. You add things and they sit there. The list gets longer and you never actually decide.
Swipe-to-rank forces a decision. You see two items from your wishlist and you swipe to say which one you want more. Do it enough times and Spree has a ranked list of what you actually want, not just what you saved.
The psychological mechanism is borrowed from pairwise comparison. It's cognitively much easier to answer "do I want this more than that?" than "how much do I want this on a scale of 1 to 10?" One question at a time. Binary answer. The ranking emerges from the comparisons.
It also just feels satisfying. Swiping through your wishlist is genuinely more enjoyable than staring at a static list. That's not an accident.
AI virtual try-on: the feature that makes Spree different
The idea was simple: tap on any clothing item in your wishlist and see it on yourself. Take a photo, or use one you already have. The AI composites the garment onto your body in a way that accounts for your shape, the angle, the lighting.
I evaluated a few approaches here. Diffusion-based try-on models have gotten genuinely impressive. I settled on an API-based integration with a specialized virtual try-on service rather than running inference locally or building a fine-tuned model myself. Running a garment try-on model locally on an iPhone would be wildly impractical at current model sizes.
The results are good enough to be useful. Not perfect — AI try-on at this stage still struggles with complex patterns and very fitted garments — but good enough that you can tell whether a piece is approximately your vibe and roughly how it might fit.
The value of virtual try-on isn't perfect photorealism. It's directional confidence. If the AI try-on looks wrong on you, the real thing probably won't work either. That's the actual use case.
Your photo never leaves the request pipeline in a way that's stored or used for training. The API I use processes and discards. No persistent storage of user photos.
The landing page: static HTML
[tryspree.app](https://tryspree.app) is static HTML and vanilla JS. No framework. No server.
I could have built it in Next.js. I know Next.js. It would have been overkill for a marketing page with no dynamic data needs. Static HTML loads faster, costs less to host, and has zero server surface area to maintain.
Use the simplest tool that solves the problem. The landing page needed to load fast, look good, and direct people to the App Store. Static HTML does all of that.
What I learned building Spree
Scraping is always messier than you expect. There's no such thing as "all product pages follow this format." Budget time for edge cases. There will be many.
Native performance isn't optional for gesture-heavy apps. The swipe mechanic is the product. It had to feel real.
AI features set expectations fast. The try-on feature is genuinely useful. It's also the first thing users notice when it's wrong. If you ship an AI feature, the failure mode is as visible as the success mode. Ship it anyway, but be transparent about its limits.
The simple version ships. I had a more complex ranking algorithm planned. I had a social feed concept. I had browser extension ideas. None of that is in the 1.0. What shipped was: paste a URL, rank your list, try on clothes. The rest can come later.
Spree is free on the App Store with an optional Pro subscription. No ads.
[tryspree.app](https://tryspree.app)