Opening documents

Declare supported files in Day.toml:

[[file_types]]
extensions = ["epub"]
mime_types = ["application/epub+zip"]
apple_uti = "org.idpf.epub-container"

Day generates the native file association. This enables Open With and, on macOS, dropping a file onto the app’s Finder or Dock icon. Use exact MIME types and extensions without leading dots. Use the established Apple UTI when one exists.

For a format the app itself defines, say so, and say how the app uses it:

[[file_types]]
extensions = ["daynote"]
mime_types = ["application/x-daynote"]
name = "file-type-daynote"   # a Fluent message: the type's name in Finder and Explorer
role = "editor"               # viewer (default) | editor | none
rank = "owner"                # alternate (default) | default | owner | none
exported = true               # the app defines this type

An exported type becomes the app’s own on Apple platforms and gets a MIME type on Linux. With rank = "owner", the Windows installer also makes the app the default for those files.

Register a receiver in the app root:

day::on_open_files(move |files| {
    day::task(async move {
        for locator in files {
            let file = FileUrl::new(locator);
            match file.read_limited(64 * 1024 * 1024).await {
                Ok(bytes) => import_book(bytes).await,
                Err(error) => show_import_error(error),
            }
        }
    });
});

import_book and show_import_error are application functions. Validate the contents and use localized error messages. The callback runs on the UI thread; native file reads run on a worker. Requests received before the root mounts are queued. Registration ends when its reactive scope is disposed.

Use the same import function for the native file picker. A reusable command can expose it in the desktop File menu with Shortcut::primary("o"), and in a mobile toolbar. See Reusable commands.

The app decides what an open request does. A reader can import into its private library, deduplicate by content hash, and call day::open_window with a stable book key on desktop. On mobile, it can navigate to the reader in the current window. File registration does not create windows or overwrite documents automatically.

Compile a bounded file reader

FileUrl belongs to the facade prelude. This function returns bytes to an app-owned importer; it does not infer a file format or display raw error text. Use it from an on_open_files callback through day::task. The other Rust block above is an integration fragment.

Complete module for src/documents.rs in the technical recipe harness. The harness compiles this exact block; it is not a standalone app. Add the catalog entries below to resource/locales/en/app.ftl and declare the module in src/lib.rs.

use day::prelude::*;

pub async fn read_import(locator: String) -> std::io::Result<Vec<u8>> {
    FileUrl::new(locator).read_limited(64 * 1024 * 1024).await
}
import_failed = The document could not be opened.

The 64 MiB bound is a recipe policy; choose a limit for your format. Map IO errors to localized messages such as res::str::import_failed() on the UI thread. Compile validation does not test file associations, provider grants, or Finder activation.

Platform differences

  • Apple bundles carry document types with their role and rank, and exported or imported UTIs. AppKit receives open-document events; UIKit receives app/scene URL contexts and imports copies. Native reads retain security-scoped access for the duration of the read.
  • Android and HarmonyOS copy granted provider files asynchronously into temporary app cache. Persist accepted content in your own storage. Staging is limited to 512 MiB.
  • Linux packages include MIME associations and file arguments in their desktop entry, and a shared-mime-info package for the app’s own types.
  • Windows MSIX and NSIS packages register Open With handlers. Windows GTK/Qt development builds emit an opt-in register.reg; their distribution packaging still requires a custom installer. The NSIS installer makes the app the default only for rank = "owner" types, and restores the previous default on uninstall.
  • macOS GTK/Qt builds with declared file types produce development app bundles for Finder/Dock testing. These reference local resources and installed toolkit libraries; they are not redistributable packages.
  • Installed web apps use the browser’s File Handling API when available. Unsupported browsers and ordinary tabs still use the file picker.

Some desktop activations start a new process rather than contacting an existing one. The editor role tells the system the app edits these files; the app still receives a copy to read, and persistent mobile provider grants are not part of this API.

The document reference covers the full platform mapping, cache lifetime, command-line delivery, manifest fields, testing, and native API references.