◉GEMINI LABJP
●ARGON — gemini-4-argon is generally available as of 10/13, aimed at complex coding, agents, math and science●DEEP RES — deep-research-pro-preview-12-2025 shuts down 10/23, 5 days left. Move to deep-research-preview-04-2026●NANO — gemini-nano-banana-2.1 is GA (10/06). gemini-3.1-flash-image is deprecated with no shutdown date yet●MCP — One write-up cut an 85,000-token tool result to 310 tokens by wrapping it in an ADK FunctionTool●FLASH — gemini-3.7-flash and 3.5-flash now route automatically to newer Flash models (10/08)●NEW — Stop Re-Reading Every Crash Report: Gre…●ARGON — gemini-4-argon is generally available as of 10/13, aimed at complex coding, agents, math and science●DEEP RES — deep-research-pro-preview-12-2025 shuts down 10/23, 5 days left. Move to deep-research-preview-04-2026●NANO — gemini-nano-banana-2.1 is GA (10/06). gemini-3.1-flash-image is deprecated with no shutdown date yet●MCP — One write-up cut an 85,000-token tool result to 310 tokens by wrapping it in an ADK FunctionTool●FLASH — gemini-3.7-flash and 3.5-flash now route automatically to newer Flash models (10/08)●NEW — Stop Re-Reading Every Crash Report: Gre…
Articles/API / SDK
◈ API / SDK/2026-05-24Advanced

Apple Vision Framework × Gemini API: Hybrid Image Recognition — Cutting Wallpaper App Cloud Inference Costs by 70%

How I built an on-device prefilter with Apple Vision Framework to cut Gemini Vision API calls by more than half in my iOS wallpaper app. Real cost, accuracy, and latency numbers, with the gotchas an indie developer hits along the way.

Gemini API250Vision4Apple Vision FrameworkHybrid InferenceiOS13Cost Optimization13Indie Developer13

✦ Premium Article

In spring 2025 I moved the automatic category classifier of my iOS wallpaper app fully onto the Gemini Vision API. Accuracy went up nicely. The month-end cloud bill, though, came in at nearly double what I had budgeted, and the balance between cost and revenue became uncomfortable. I'm Hirokawa Masaki — an artist and indie developer who has been shipping mobile apps personally since 2014, with cumulative downloads across my apps now past 50 million. Once a single API path is processing that much traffic, the limits of an "all-cloud" inference design start to show up in the numbers very quickly.

So over the next three months I rebuilt the pipeline as a hybrid setup: an on-device prefilter on iOS using Apple Vision Framework, with only the images that actually need rich semantic understanding being forwarded to the Gemini Vision API. In production, monthly Gemini Vision API calls dropped by 53% and total Gemini cost dropped by roughly 71%.

Here are the design choices, the implementation details, and the operational gotchas I ran into. If you are an iOS developer trying to balance cloud inference quality against cost, I hope some of this is useful.

Why all-cloud inference had to go

The first version was the simplest one: every uploaded wallpaper was sent to Gemini Vision and classified into one of 18 categories (nature, abstract, person, animal, vehicle, architecture, space, and so on). Accuracy was strong — Cohen's kappa against a human-labeled set landed at 0.81.

Within a month of running it in production, three problems showed up.

The first was cost. My wallpaper app receives roughly 18,000 new image uploads per day. At about 0.0011 USD per image with Gemini Vision, that comes to north of 600 USD per month. Stacked against AdMob revenue from a personal-scale wallpaper app, the math was getting tight.

The second was latency. The round trip to the API ranged from 400 ms to 1,200 ms, which directly translated into the "wait until category appears" experience for the user.

The third was waste. A non-trivial share of incoming images was clearly not worth classifying — nearly-solid-color frames, blurry test shots, very small thumbnails, and (it turns out) screenshots of wallpapers my own app had already produced. Spending API credits on those felt like burning paper.

That was the moment I committed to a "not every image goes to the cloud" architecture.

How the on-device prefilter's responsibility is scoped

Apple Vision Framework has shipped on iOS since iOS 11, and in the recent iOS 26 line VNClassifyImageRequest, VNGenerateAttentionBasedSaliencyImageRequest, and VNDetectHumanRectanglesRequest all run on the Apple Neural Engine. On an iPhone 14 Pro I measured 30–90 ms latency, and the energy cost is barely visible in Instruments. It is also free.

But the precision ceiling is real. The general-purpose classifier in Apple Vision lives in a roughly 1,000-label ImageNet space, and the taste-driven categories my wallpaper app actually needs — "Japanese painting style," "wagara pattern," "cyberpunk," "lofi" — are not in that vocabulary. This is exactly where Gemini Vision earns its keep.

So I split the responsibilities like this:

The on-device side acts as a gatekeeper that decides whether sending the image to Gemini Vision is worth it. Specifically:

  1. Score sharpness and saliency area, drop frames that are obviously low quality
  2. Detect images that were generated by my own app (watermark check) and skip them
  3. Get a coarse genre (photo-like, illustration-like, abstract) from the generic Vision classifier, and pass that as a hint to the Gemini prompt

The cloud side (Gemini Vision API) only sees images that made it past the gate, and is purely responsible for final category, mood, recommended tags, and dominant color palette.

The framing is not "let AI do everything." It is "let AI pick which work belongs to AI." Both of my grandfathers were temple carpenters in Japan, and that "right tool for the right job" sensibility tends to shape how I design software. This split felt natural.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦The routing design that cut Gemini Vision API calls by 53% with an on-device prefilter
✦Where the boundary between VNClassifyImageRequest and Gemini Vision was drawn, and why
✦Hard-won gotchas from running this in a wallpaper app with 50 million cumulative downloads
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Gemini Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

◈ API / SDK2026-05-26
Running Gemini 3 Pro and Flash in Parallel on AdMob Monthly Reports — Notes from Ten Mornings
Implementation notes from sending the same AdMob monthly report to Gemini 3 Pro and Gemini 3 Flash in parallel for ten days. Cost, latency, and summary quality compared from an indie developer's perspective.
◈ API / SDK2026-05-21
Designing Event-Driven AI Workflows with Gemini API and Cloud Pub/Sub — Notes from an Indie Developer
An implementation memo on wiring Gemini API into Cloud Pub/Sub event-driven workflows. Using an app-review analysis pipeline as the running example, the article covers retry policy, dead-lettering, idempotency, and cost guardrails — from the perspective of someone running it solo.
◈ API / SDK2026-10-14
When Your config Raises "Extra inputs are not permitted", Check the Spelling of Every Key
If a google-genai call fails with a pydantic ValidationError (extra_forbidden) the moment you pass a config, it is almost always a one-letter typo. Here is the output I reproduced, a small function that checks keys before the call, and how to tell why response.text came back as None.