Skip to content

feat(imagepicker): use PHPickerViewController on iOS, add onProgress - #681

Open
sitefinitysteve wants to merge 9 commits into
NativeScript:mainfrom
sitefinitysteve:feat/imagepicker-phpicker
Open

sitefinitysteve wants to merge 9 commits into
NativeScript:mainfrom
sitefinitysteve:feat/imagepicker-phpicker

Conversation

@sitefinitysteve

@sitefinitysteve sitefinitysteve commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

What

Replaces the QBImagePickerController CocoaPod on iOS with the system PHPickerViewController, and adds an optional onProgress callback on both platforms.

Why

  • QBImagePicker is unmaintained and needs a forked pod. PHPickerViewController ships with iOS 14+, so the plugin no longer has a CocoaPods dependency at all.
  • The system picker runs out of process. It can be presented without photo-library permission, the user gets Apple's own UI (search, albums, Limited-access banner), and the app only ever receives the items the user picked.
  • Apps have no way to show "loading 2 of 5" today. iCloud-backed items can take seconds to download, and the plugin resolved everything silently.

iOS behaviour

  • authorize() still requests library access. When granted, picks resolve to their PHAsset as before.
  • When access is denied, Limited, or authorize() was never called, present() still opens the picker. Items outside the app's access fall back to a copy handed over by the picker's item provider, stored in the temp folder and exposed through path / asset. A single selection can mix both. This is documented in the README.
  • present() waits for the app to be active and presents on the main thread; the picker is dismissed before the promise resolves.
  • File copies and iCloud downloads run off the main thread.
  • minimumNumberOfSelection, showsNumberOfSelectedAssets, prompt, numberOfColumnsInPortrait and numberOfColumnsInLandscape are accepted but ignored, since the system picker owns its UI. They are marked @deprecated in the typings and README.
  • Requires iOS 14+. nativescript.platforms.ios bumped to ~9.0.0.

Android

  • onProgress emits a single fraction: 1 per item once it is ready (there is no download progress to report).
  • The copy into copyToAppFolder now uses File.copy() off the main thread instead of a synchronous read/write.

Demo app

The demo page was reworked to show the progress ticks, render multiple picks in one scrolling list, and add a Clear button. It presents the picker regardless of the permission result so the no-access path can be exercised.

One thing to be clear about: the progress bar in the demo (and in the recording) is paced. Items already on the device resolve in a few milliseconds, so the bar would jump straight to 100% and there would be nothing to see. The demo queues every onProgress tick it receives and holds each one on screen for about 600 ms before showing the next. The values it displays are exactly what the plugin reported, only the timing is slowed for the eye. The plugin side is not faked: onProgress is a real stream from PHPickerViewController's item provider (or PhotoKit when the app has library access), and an iCloud item on a real device produces several ticks with increasing fractions as the download runs. An app that subscribes to it can drive a progress bar directly without the demo's pacing.

Recording

packages/imagepicker/images/imagepicker-ios-phpicker.mov is a 19 second recording from an iPhone 15 Pro Max with photo access set to None: the picker opens, iOS shows the "Private Access to Photos" banner, and the picks come back as file copies with progress rows. If you would rather not carry the binary in the repo, say so and I will drop that commit and attach the clip here instead.

Tested

  • iPhone 15 Pro Max, photo access Full and None (single and multiple picks, iCloud items showing progress).
  • iPhone 17 Pro simulator, access granted and revoked.
  • Android demo app builds. The Android changes are limited to the copy path and the progress callback.

Replace the QBImagePickerController pod with the system photo picker
(iOS 14+). The public API is unchanged: create(), authorize(), present()
and the ImagePickerSelection shape all behave as before, and cancel still
rejects with Error('Canceled').

- No CocoaPods dependency any more; the picker runs out of process.
- authorize() is now optional. With library access, picks resolve to
  their PHAsset as before; without it, the picker's file copy is used.
- Ordered multi-select honours maximumNumberOfSelection.
- The iOS-only UI options (prompt, column counts, etc.) are marked
  deprecated since the system picker owns its own UI.
iOS copies now run on a global dispatch queue and Android uses the
async File.copy(), so large videos no longer block the UI while the
picker resolves. Android also drops the two duplicated inline handlers
in favour of a shared toSelection() helper. Public API is unchanged.
iOS streams per-item download progress (iCloud items via PhotoKit and
item-provider NSProgress); Android has nothing to report so it emits a
single 100% per item. Demo shows a progress bar while items resolve.
…ving; demo: fix first-tap no-op, no cap, single scroll, clear button
Adds a 19 second screen recording from an iPhone 15 Pro Max to
packages/imagepicker/images and links it from the README demo section.

Why a video in the repo: the PHPicker change is easiest to judge by
watching it. The recording was taken with the app's photo access set to
None, so it shows the part that is hard to reproduce on a simulator: the
system picker still opens, iOS shows its "Private Access to Photos"
banner, the selected items come back as file copies, and the demo's
progress rows update as each item loads. Reviewers can check the
behaviour without setting up a device.

The file is 8.4 MB, which is why it lives next to the other plugin media
(compare packages/animated-circle/images) rather than being embedded in
the README.
@coderabbitai

coderabbitai Bot commented Sep 11, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 815a6fdd-4d45-4c19-8741-21a04cc9d96b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sitefinitysteve

Copy link
Copy Markdown
Contributor Author
ScreenRecording_09-11-2026.13-56-49_1.mov

Here's what it looks like, the progress streaming is faked, they're local images, but... it's working.

Much nicer picker and gives access to the phones collections where the previous one didn't (not to mention removing a cocoapod)

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant