DEV Community

Ethan
Ethan

Posted on • Originally published at opennomos.com

The Architecture of On-Device Privacy — How Swipe Cleaner Keeps Your Photos Local

Disclosure: I work on Nomos, the ecosystem behind Swipe Cleaner.

The Problem with Photo Cleaners

Most photo cleaning apps follow the same pattern: upload your photos to a cloud server, run ML models server-side, send back results. It's convenient for developers — you get unlimited compute, easy model updates, simple analytics.

It's terrible for users.

Your photos are the most personal data on your phone. Medical records, family moments, private documents, embarrassing screenshots. Uploading them to someone else's server means trusting that company with literally everything. And we've seen how often that trust is broken.

The Architecture Decision

When we built Swipe Cleaner, we made one decision that shaped everything else:

All processing happens on the device.

Not "we encrypt data in transit." Not "we delete after processing." Not "trust us, we're different."

None of it. The data never leaves.

This sounds simple until you try to build it. Here's what it actually requires.

Core ML on iOS: The Technical Reality

Apple provides excellent on-device ML infrastructure through Core ML and the Neural Engine. Modern iPhones have dedicated ML hardware that can run models with impressive performance.

But "it works on paper" and "it works in production" are very different things.

Model Size Constraints

A cloud model can be gigabytes. An on-device model needs to run on a phone that's also running iOS, background apps, and maybe Spotify. Our image classification model is under 50MB — achieved through aggressive quantization and architecture optimization.

We use Core ML Tools for model conversion, with INT8 quantization as the default. For the blur detection and duplicate detection models, FP16 precision provides a good balance of accuracy and speed.

let config = MLModelConfiguration()
config.computeUnits = .cpuAndNeuralEngine
let model = try SwipeCleanerModel(configuration: config)
Enter fullscreen mode Exit fullscreen mode

Memory Management

Processing a library of 10,000+ photos without crashing requires careful memory management. We batch process in groups of 50 images, releasing memory between batches:

func processBatch(_ images: [UIImage]) async throws -> [Classification] {
    return try await withCheckedThrowingContinuation { continuation in
        DispatchQueue.global(qos: .userInitiated).async {
            let results = images.compactMap { classify($0) }
            continuation.resume(returning: results)
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Real-Time Analysis

Users expect instant feedback. Swiping through photos should feel native. We achieve this through:

  1. Progressive loading: Show thumbnails immediately, analyze in background
  2. Priority queuing: Photos visible on screen get analysis priority
  3. Result caching: Once analyzed, results are stored locally in Core Data

Privacy by Architecture, Not Policy

This is the part that matters philosophically.

A privacy policy is a promise. Architecture is a guarantee. When you say "we don't collect your data" in a privacy policy, users have to trust that a) you're telling the truth, and b) you won't change your mind later.

When your app literally can't send data because there's no network code for photo upload, privacy becomes a property of the system, not a promise from the company.

What We Don't Have

  • No photo upload pipeline
  • No cloud storage for user photos
  • No analytics tracking which photos users clean
  • No third-party SDKs that exfiltrate data

These aren't features we turned off. They're features we never built.

What We Do Have

  • On-device ML pipeline (Core ML + Neural Engine)
  • Local Core Data storage for analysis results
  • iCloud sync for preferences (opt-in, encrypted)
  • On-device photo comparison (pixel-level, never leaves device)

The Trade-offs

Being honest about the downsides:

Model updates require app updates. Cloud models can be updated silently. We have to ship a new app version when we improve our ML models.

Device compatibility varies. iPhone 12 runs the same models slower than iPhone 16. We handle this with runtime performance detection and model selection.

No usage analytics. We genuinely don't know which features users use most. We rely on App Store reviews and direct feedback.

Harder debugging. When something goes wrong, we can't just check server logs. We built an opt-in debug mode that users can share voluntarily.

Why This Matters More in 2026

The AI industry is moving toward more data collection, not less. Every new model launch comes with questions about training data, inference privacy, and data retention. Apple Intelligence is pushing on-device as a differentiator, but most third-party apps still default to cloud.

Building privacy-first isn't a marketing angle. It's a technical choice with real engineering consequences. It costs more in development time, limits some product decisions, and requires justifying why you can't add certain features.

But the alternative — asking users to trust you with their most personal data — is a responsibility most apps shouldn't carry.

Open Questions

We're still figuring out:

  • How to do collaborative ML improvement (federated learning?) without compromising privacy
  • Better on-device model compression for older devices
  • Privacy-preserving ways to understand feature usage

If you're building something similar, I'd love to hear how you're solving these problems.


Swipe Cleaner is part of the Nomos ecosystem. All code examples are simplified for clarity.

Top comments (0)