The photos bridge shows the system photo picker, and browses the photo library — pages, thumbnails and full-size exports — as files in a file root.
dotnet add package Shiny.AppDeviceBridge.Photos
builder. UseAppDeviceBridge (
bridge => bridge. AddPhotosBridge (),
Client
C#
TypeScript
IPhotosBridge
AddPhotosBridgeClient()
PhotosBridge
Topic
Details
Picker
POST photos/pick shows the system photo picker — no permission needed — and copies what the user chose into a file root (cache by default) under photos/. The answer lists each as a { root, path } the page reads through the files bridge. An empty list means the user cancelled. Every head has a picker.
Library
browse every photo on the device, newest first: GET photos/library?offset=&limit= (200 at most), GET photos/library/{id}/thumbnail?size= for a JPEG that fits a square (32–1024 px), and POST photos/library/{id}/export to copy the original into a file root. It needs access — POST photos/access — and answers 403 without it.
Platforms
PhotoKit on iOS, Mac Catalyst and macOS; MediaStore on Android; the user’s Pictures folder on Windows. Linux has no photo library, so the library endpoints return 501 there.
Platform setup
NSPhotoLibraryUsageDescription on Apple platforms, plus the com.apple.security.personal-information.photos-library entitlement where the app is sandboxed. READ_MEDIA_IMAGES on Android 13 and later, READ_EXTERNAL_STORAGE before.
Picked and exported photos are copied into a file root, so the page reads them through the
files bridge like any other file.
import { PhotosBridge, FilesBridge } from "@shinyorg/appdevicebridge" ;
const photos = new PhotosBridge ();
const picked = await photos. pick ({ limit: 3 , root: "cache" }); // the system picker, no permission needed
const files = new FilesBridge ();
for ( const p of picked) {
const blob = await files. readBytes (p.file.root, p.file.path);
gallery. append (Object. assign ( new Image (), { src: URL . createObjectURL (blob) }));
GET /_bridge/ photosWhat works here, and whether the app has library access — without prompting. POST /_bridge/ photos/accessRequests photo library access. POST /_bridge/ photos/pickShows the system photo picker and copies what the user chose into a file root. GET /_bridge/ photos/libraryA page of the photo library, newest first. GET /_bridge/ photos/library/{id} /thumbnailA JPEG thumbnail that fits a square of size pixels, 32–1024. POST /_bridge/ photos/library/{id} /exportCopies a library photo at full size into a file root. What works here, and whether the app has library access — without prompting.
C# IPhotosBridge.GetStatusAsyncTS PhotosBridge.getStatus
Response 200 PhotoStatus PhotoStatus · 3 fields pickerSupportedboolean Whether the system photo picker is available. librarySupportedboolean Whether the photo library can be browsed here. libraryAccessAccessState Library access. Restricted is a limited selection the user chose on iOS or Android.
POST /_bridge/ photos/access# Requests photo library access.
C# IPhotosBridge.RequestAccessAsyncTS PhotosBridge.requestAccess
Response 200 PhotoAccessResult PhotoAccessResult · 1 field POST /_bridge/ photos/pick# Shows the system photo picker and copies what the user chose into a file root. Empty when the user cancels. Not every platform enforces limit.
C# IPhotosBridge.PickAsyncTS PhotosBridge.pick
Body application/json · PhotoPickRequest PhotoPickRequest · 2 fields limitnumber How many the user may choose, 1–50. rootstring The file root the photos are copied into.
Response 200 PhotoFile []PhotoFile · 4 fields fileBridgeFile Where it is: read it through the files bridge. fileNamestring The name it had on the device, where the platform says. contentTypestring | null sizenumber
GET /_bridge/ photos/library# A page of the photo library, newest first. Fails with 403 without library access.
C# IPhotosBridge.GetLibraryAsyncTS PhotosBridge.getLibrary
Parameter In Type offsetquery number optional limitquery number optional
Response 200 PhotoPage PhotoPage · 4 fields itemsLibraryPhoto []offsetnumber limitnumber hasMoreboolean Whether another page follows.
Errors 403 ForbiddenGET /_bridge/ photos/library/{id} /thumbnail# A JPEG thumbnail that fits a square of size pixels, 32–1024.
C# IPhotosBridge.GetThumbnailAsyncTS PhotosBridge.getThumbnail
Parameter In Type idpath string required sizequery number optional
Response 200 Binary content — Blob in TypeScript, Stream / byte[] in C# POST /_bridge/ photos/library/{id} /export# Copies a library photo at full size into a file root.
C# IPhotosBridge.ExportAsyncTS PhotosBridge.export
Parameter In Type idpath string required
Body application/json · PhotoExportRequest PhotoExportRequest · 1 field rootstring The file root the photo is copied into.
Response 200 PhotoFile PhotoFile · 4 fields fileBridgeFile Where it is: read it through the files bridge. fileNamestring The name it had on the device, where the platform says. contentTypestring | null sizenumber
This bridge raises no events — everything it does is a request and its response.
JSON property names are camelCase and enums travel as their names. A ? marks a property the payload may omit;| null one that may be null.
pickerSupportedboolean Whether the system photo picker is available. librarySupportedboolean Whether the photo library can be browsed here. libraryAccessAccessState Library access. Restricted is a limited selection the user chose on iOS or Android.
Where a permission stands. Reported by every bridge that needs one; the names match Shiny's own.
"Unknown""NotSupported""NotSetup""Disabled""Restricted""Denied""Available"
limit?number How many the user may choose, 1–50. root?string The file root the photos are copied into.
A photo copied into a file root.
fileBridgeFile Where it is: read it through the files bridge. fileNamestring The name it had on the device, where the platform says. contentTypestring | null sizenumber
A file the page can reach through the files bridge: a named root and a path inside it. Bridges that take or return a file use this, so nothing reaches the page that /_bridge/files would not also serve.
itemsLibraryPhoto []offsetnumber limitnumber hasMoreboolean Whether another page follows.
A photo in the library.
idstring Pass to getThumbnail and export. createdAtstring | null widthnumber heightnumber fileNamestring | null
PhotoExportRequestobject # root?string The file root the photo is copied into.
These routes sit under AppDeviceBridgePolicies.Bridges like every other bridge: callers on this device by default.
See Security .