Put App Documents in the System File Browser
An invoice, drawing, report, or project file can live inside an app for years without ever becoming a document the operating system understands. Users still have to open the app before another app can preview or share that file.
Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from one Java or Kotlin codebase.
PR #5607 adds com.codename1.documents. It publishes a selected, read-only document tree into the iOS Files app and the Android storage picker. The app chooses which nodes exist, what they are named, and whether the bytes are already on disk or fetched on demand.
Publish a tree, not a directory permission
The root is a DocumentNode. Folder and file nodes under it define what the system browser sees:
String shared = DocumentProvider.getSharedDirectory();
writePdf(shared + "/august-1042.pdf", invoiceBytes);
DocumentNode root = DocumentNode.folder("root", "My Invoices");
DocumentNode august = DocumentNode.folder("2026-08", "August 2026");
august.add(DocumentNode.file("inv-1042", "Invoice 1042.pdf")
.setContentType("application/pdf")
.setPath("august-1042.pdf")
.setSize(invoiceBytes.length)
.setLastModified(invoiceModified));
root.add(august);
DocumentProvider.publish(root);
The published tree does not have to match internal storage. An object id can become a stable document id. A database title can become the visible filename. A folder can be a projection of records that are not folders on disk.
The location is read-only. The file browser cannot rename, delete, or overwrite files the app cannot honor. To change the view, publish a new tree.
This is narrower than exposing the whole documents directory. The browser only sees nodes from the latest published index. Logging out can withdraw the location:
DocumentProvider.clear();
If the goal is simply to show the app’s entire iOS documents directory, UIFileSharingEnabled and LSSupportsOpeningDocumentsInPlace remain the smaller option. Document Provider is for a curated tree, a virtual layout, or content that is not on the device yet.
The iOS extension cannot call the app
Apple runs a File Provider extension in a separate process. Files can start it while the Codename One app is not running. The extension cannot call into Java to ask what a folder contains.
So Codename One uses a publication model. DocumentProvider.publish() serializes the tree into an App Group container shared by the app and the generated extension. The extension reads that index and serves the browser on its own.
On Android, the generated DocumentsProvider runs inside the app process, but it still uses the same published model. One model keeps app code from depending on Android’s different process boundary.
Local files must be written under DocumentProvider.getSharedDirectory(). The iOS extension cannot see paths under the app’s ordinary home directory.
Do not overwrite a file that is already published. Another app may be reading it. Write the new version under a new name, publish a tree that points to it, then delete the old bytes after readers have had time to finish.
Remote nodes can appear before their bytes arrive
A file node can carry a remoteId instead of a local path:
DocumentProvider.setRemoteEndpoint(
"https://api.example.com/drive", sessionToken);
DocumentNode root = DocumentNode.folder("root", "Example Drive");
root.add(DocumentNode.file("report-52", "Quarterly Report.pdf")
.setContentType("application/pdf")
.setRemoteId("server-object-52")
.setSize(487120)
.setLastModified(reportModified));
DocumentProvider.publish(root);
When the user opens that node, the extension sends:
GET /drive/fetch?id=server-object-52
Authorization: Bearer <published token>
The token is the extension’s only app state. It cannot reuse an in-memory login session from a process that may not exist. Publish a new endpoint token when the session renews, and clear the provider on logout.
A node can have both a local path and a remote id. The local copy wins, so a cached document opens without a network trip, while the same index still describes how to fetch uncached files.
iOS signing is part of the feature
Referencing com.codename1.documents generates the platform machinery. On iOS, set the hint so the Certificate Wizard and preflight prepare extension signing before bytecode scanning:
codename1.arg.ios.documentProvider.enabled=true
codename1.arg.ios.documentProvider.appGroup=group.com.example.myapp
codename1.arg.ios.documentProvider.displayName=Example Drive
The extension has its own bundle id, <your.package>.CN1Documents, and needs its own App ID and provisioning profiles. Both the app and extension App IDs must use the same App Group. The Certificate Wizard creates these assets. A wildcard App ID cannot carry the App Group entitlement.
Android needs no extra configuration beyond using the API. The build declares an authority at <your package>.documents.
Mac Catalyst cannot host Apple’s FileProvider framework. DocumentProvider.isSupported() returns false there and in the simulator. Simulator publication still writes the generated index under the app home directory so you can inspect what a device extension would receive.
Narrow capabilities by design
Document Provider gives the OS access to a named tree, not the whole app sandbox. A read-only provider, an explicit App Group, and a tree that disappears on logout reduce the amount of platform code a team has to get right.

Reactions
Share this post
0 replies
Sign in with your Maxlayer account to join the conversation.