Skip to content

iOS: would you accept an opt-in capture controller that preserves original colors? #174

Open

Description

@juanlabrador

iOS: would you accept an opt-in capture controller that preserves original colors?

The problem

On iOS the plugin uses Apple's VNDocumentCameraViewController. For document
scanning it's excellent, but for identity verification (KYC) it has three
limitations we couldn't work around from the outside:

  1. It applies automatic filters that wash out colors. The returned image is
    processed for readability, not for fidelity. For an ID card that means shifted
    tones and lost detail — some verification providers reject those images.
  2. The filter buttons can't be removed. Users can switch to grayscale or
    black & white, which silently breaks the same flows.
  3. Multi-page capture is forced. For a single-sided ID it's confusing UX, and
    the user has to tap "Save" after every shot.

Android already has an escape hatch for the first point — AndroidScannerMode.base
keeps real colors, which is exactly why we use it. iOS has no equivalent, because
the limitation lives inside Apple's controller rather than in this plugin.

What we built, and why we're asking

We maintain a fork where iOS uses a custom capture controller instead of Apple's. The
pipeline is:

  1. Preview with AVCaptureSession + AVCapturePhotoOutput.
  2. On shutter tap, capture a raw JPEG — no filters applied.
  3. Run VNDetectDocumentSegmentationRequest (Vision framework, the same model Apple's
    own Notes app uses) to find the document's four corners.
  4. Apply CIPerspectiveCorrection with those corners to the raw image, so the
    result is cropped and deskewed with the original colors.
  5. If detection fails, return the flat photo rather than failing the capture.
  6. Show a confirmation preview with Retake / Use.

It's been running in production for a while in an identity-verification flow.

We'd rather contribute this than keep a fork, but we don't want to send 500+ lines
of Swift you may not want
, so we're asking first.

Questions before we write anything

  1. Is this in scope? It's a second capture path, not a change to the existing one.
    Our intent is that it be strictly opt-in — e.g. an IosScannerMode alongside
    the existing AndroidScannerMode — so current behaviour stays untouched by default.

  2. How would you want it shaped? Our fork is based on 1.4.0, and iOS has been
    restructured since (SPM layout, CunningDocumentCropperViewController,
    CroppingOverlayView, MagnifierView). We'd port onto the current architecture
    and reuse what's already there rather than duplicating it — but you know better
    than us where the seams should be.

  3. Is a framing guide overlay welcome, and if so how? Our version draws a centered
    rectangle with a fixed aspect ratio plus a dimmed surround, which materially improves
    capture quality for cards. In our fork the ratio is hardcoded to the ID card format we
    needed. If you'd want it, we'd expose the ratio as a parameter — or we can
    leave the overlay out entirely and contribute only the capture path.

  4. Anything you'd want us to avoid? Minimum iOS version, dependencies, testing
    approach — happy to follow whatever you prefer.

If you'd rather not take this direction, that's completely fine — knowing now saves us
both time, and we'll keep maintaining our fork.

Thanks for the project, and for how actively you keep it maintained.

Activity

  1. vicajilau commented on Oct 21, 2026

    @vicajilau
    Owner

    Thanks for asking before writing the code, and for such a clear proposal. Yes, this is in scope: iOS has no equivalent to AndroidScannerMode.base, and an opt-in path that keeps the original colors is a gap worth closing.

    One thing that should make the contribution much smaller than your fork: most of steps 3 to 6 of your pipeline already exist in the plugin. The gallery cropper (CunningDocumentCropperViewController) detects the corners with VNDetectDocumentSegmentationRequest, applies CIPerspectiveCorrection, lets the user adjust the corners, and confirms each page. When detection fails it falls back to default corners the user can drag, rather than returning the flat photo. With IosScannerOptions(defaultFilter: IosDocumentFilter.original, showFilterBar: false) it keeps the original colors and hides the filter selector. So what is really missing is the capture.

    Here is the shape I would suggest:

    1. Capture only. A controller built on AVCaptureSession and AVCapturePhotoOutput that takes an unprocessed photo and hands it to the existing cropper, instead of a separate detection, correction and Retake/Use screen. If a retake action is needed, adding it to the cropper is better than a second confirmation screen. With noOfPages: 1 the flow ends after the first confirmed photo, which covers the single-sided ID case.
    2. Strictly opt-in, through IosScannerOptions. For example an IosScannerMode with visionKit as the default and a mode for this capture path, added to IosScannerOptions rather than as a new top-level parameter of getPictures.
    3. No framing overlay in the first PR. It is quite specific to ID cards. Once the capture path is in, a follow-up PR with the aspect ratio as a parameter would be welcome.
    4. Minimum iOS version. The plugin supports iOS 13. The cropper already runs the segmentation request only on iOS 15+ and falls back to default corners below that, so following the same pattern is fine.
    5. No new dependencies, and please describe in the PR how it was tested on physical devices, including orientation changes, iPad, interruptions such as a phone call or the app going to the background, and denied camera permission. The plugin already requests camera access natively (withCameraAccess in CunningDocumentScannerPlugin.swift), so please reuse it.

    Happy to review a draft PR early if you want feedback on the structure before polishing it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions