FilePicker Plugin for NativePHP Mobile#
Native document picker: select one or many files of any type from the
device — iOS UIDocumentPickerViewController (Files app, iCloud, providers),
Android Storage Access Framework (ACTION_OPEN_DOCUMENT). Picked files are
copied into app-local storage by the native side, so the paths PHP
receives are durable and readable without permission bookkeeping (no
security-scoped URLs, no content:// grants to manage). No runtime
permissions are required on either platform.
Installation (path package inside this repo)#
composer require noehassiel/filepickerphp artisan vendor:publish --tag=nativephp-plugins-provider # oncephp artisan native:plugin:register noehassiel/filepickerphp artisan native:plugin:list # verify
Then rebuild: php artisan native:run ios|android.
Usage#
use Noehassiel\FilePicker\Facades\FilePicker; // Any file type, multi-selectFilePicker::open(); // Single fileFilePicker::open(multiple: false); // Restricted types — UTIs on iOS, MIME types on AndroidFilePicker::open(types: ['com.adobe.pdf']); // iOSFilePicker::open(types: ['application/pdf']); // Android
The call returns immediately; the selection arrives as an event:
use Noehassiel\FilePicker\Events\FilePickerCancelled;use Noehassiel\FilePicker\Events\FilesPicked;use Native\Mobile\Attributes\On; #[On(FilesPicked::class)]public function handleFiles(array $files): void{ // $files: [{path, name, mimeType, size}] — app-local copies} #[On(FilePickerCancelled::class)]public function handleCancel(): void {}
Events#
| Event | Payload | Description |
|---|---|---|
FilesPicked |
files: [{path, name, mimeType, size}] |
Files copied into app storage |
FilePickerCancelled |
— | User dismissed the picker |
Usage (JavaScript / Inertia)#
import { open, Events } from 'noehassiel-filepicker'; // Resolves once the picker is PRESENTED, not once the user has chosen.await open({ multiple: true }); on(Events.FilesPicked, ({ files }) => { // [{ path, name, mimeType, size }, ...] — already copied into app storage files.forEach(upload);}); on(Events.FilePickerCancelled, () => { // user dismissed});
Notes#
- Copies land in
Application Support/FilePicker/(iOS) andfilesDir/FilePicker/(Android) under UUID-prefixed names. Callers own cleanup after consuming them (the app's outbox stages + deletes them). - Android hosts the SAF round-trip in a transparent
FilePickerActivity(declared innativephp.json) because bridge functions cannot receiveonActivityResult.
Camera and photo library (iOS)#
capturePhoto() and pickMedia() open the camera and the photo library, presented on the
topmost view controller — so they work from inside a <native:bottom-sheet>, including a
sheet over another sheet.
use Noehassiel\FilePicker\Facades\FilePicker; FilePicker::capturePhoto();FilePicker::pickMedia(multiple: true, maxItems: 10, mediaType: 'images');
Both land on the same FilesPicked event as open() — a photograph is a file, so a consumer
needs one listener no matter where the file came from:
#[On(FilesPicked::class)]public function handleFilesPicked(array $files): void{ // [['path' => ..., 'name' => ..., 'mimeType' => ..., 'size' => ...], ...]}
Why these exist#
nativephp/mobile-camera presents on the window's bare rootViewController without walking the
presentation chain, and UIKit silently ignores present() on a controller that is already
presenting. Its camera and gallery therefore do nothing when launched from a bottom sheet —
reported as NativePHP/mobile-camera#11.
These two are iOS only. Android's camera goes through Activity result launchers and never had
the problem, so keep using nativephp/mobile-camera there. Once upstream ships the fix, you can
drop back to it on both platforms.
NSCameraUsageDescription is declared by this plugin. The photo library needs no usage string:
PHPickerViewController runs out of process and returns only what the user chose, so no
photo-library authorization is involved.
License#
Proprietary. All rights reserved © noehassiel. See LICENSE. Redistribution, resale, or republishing of this package — in source or compiled form — is not permitted without prior written permission from the copyright holder.