day-piece-camera 0.1.0

A camera viewfinder with photo capture for Day apps on iOS, Android and HarmonyOS
Documentation

day-piece-camera

ci License

Overview and capabilities

day-piece-camera adds a live camera preview and JPEG photo capture to a Day app.

Day is a Rust framework for building applications from a shared codebase using each platform's native UI toolkit. A piece is a UI component you place in a layout; a part provides a capability without drawing UI. Day's day command builds and packages the Rust code, resources, and native platform code together. Cargo.toml declares Rust dependencies; Day.toml configures the app and its target platforms.

The camera() piece supports front/back camera switching, reactive session control, a capture trigger, and signals reporting session state and the latest photo. A signal is observable state: changing one updates the UI that reads it. A trigger is an event you fire, such as a shutter press.

Photos are files in the app's cache directory, with a path, width, and height in Photo. Photo::bytes() reads a file into Arc<Vec<u8>>, which can be displayed with Day's day::async_image. The crate does not save to the photo library; move or copy captures to persistent storage if the app needs to keep them.

Platform support and limitations

Day target Implementation Limitations
ios-uikit AVFoundation capture sessions, preview and photo output Physical device required for capture; the iOS Simulator reports Unavailable. Below iOS 17, the preview is fixed portrait.
android-material CameraX (androidx.camera), preview and image capture; architecture guide Requires a usable camera and permission; the emulator's virtual camera supports the capture path.
harmony-arkui ArkUI surface and OpenHarmony native camera API (OH_Camera) Compiles and links, but is unverified on hardware.
Desktop, web, mock No camera renderer The piece becomes a placeholder; support() returns Unsupported.

day_piece_camera::support() reports whether a renderer was compiled in, not whether hardware or permission is available. Read CameraState at runtime: Idle, Starting, Running, Unavailable, Denied, or Error(String). Torch, zoom, and video recording are not exposed.

Add it to a Day project

Start with a Day app (the demo shows its complete structure). Add this to its Cargo.toml:

[dependencies]
day-piece-camera = "0.1"

Add an app-specific camera explanation to Day.toml, merging it into any existing [permissions] table:

[permissions]
camera = "Take photos to attach to your notes."

Alternatively, use camera = true and supply permission_camera in the app's translation catalogs, as the demo does. The crate declares the permission it uses; Day generates the Android, iOS, and HarmonyOS permission entries. iOS and HarmonyOS builds require the app's explanation.

Return this piece from your app's UI-building function or place it in a layout:

use day::prelude::*;
use day_piece_camera::{camera, CameraState, Photo};

fn camera_panel() -> impl Piece {
    let shutter = Trigger::new();
    let state = Signal::new(CameraState::Idle);
    let photo: Signal<Option<Photo>> = Signal::new(None);

    column((
        camera()
            .auto_request()
            .capture(shutter)
            .state(state)
            .photo(photo)
            .frame(320.0, 240.0),
        button("Take photo")
            .enabled(move || state.get() == CameraState::Running)
            .action(move || shutter.notify()),
    ))
}

.auto_request() opts into the system permission prompt when the session is first wanted. Without it, request permission yourself through day-part-permissions; an undecided permission leaves the camera idle. .active(signal) starts/stops the session, .facing(signal) switches cameras, and removing the piece stops its session. Gate camera navigation with support() on apps that also target desktop or web.

With the chosen target configured in your app and its platform SDK installed, run day build -p android-material or day launch -p ios-uikit. Day reads the crate's backend metadata and enables its matching feature; you do not need to forward camera features in your app. Use Day's build pipeline to include the native sources.

Architecture and dependencies

Dependency links below lead to upstream source repositories or official API documentation. Version requirements describe this checkout's Cargo.toml, not necessarily the newest upstream releases.

src/lib.rs defines the portable piece, properties, commands, permission checks, and event decoding. Reactive changes become CameraPatch commands (Start, Stop, Capture, Facing). Native callbacks become Day custom events that update the app's state and photo signals on the UI thread. Platform renderers register at link time through linkme.

Layer Dependencies and responsibility
Shared Rust day-core for the piece/tree, day-spec for types/events, day-pieces for builders, day-reactive for bindings, day-part-permissions for permission checks, log for diagnostics, linkme for registration.
iOS Optional day-toolkit-uikit, objc2 0.6 and objc2-ui-kit 0.3. A Swift shim calls system AVFoundation; Day stages it into the app's Swift build.
Android Optional day-toolkit-material provides the JNI/backend boundary. Gradle adds CameraX camera-core, camera-camera2, camera-lifecycle, and camera-view, each at 1.5.1, plus their transitive dependencies.
HarmonyOS Optional day-toolkit-arkui and build dependency cc 1. build.rs compiles the C++ camera shim; the native implementation links ohcamera, ohimage, and native_window. Day stages the ArkTS surface code.
Tests day-mock supplies the headless test backend.

The iOS shim configures the session on a serial queue and reports on the main queue. Android owns a separate camera lifecycle so navigating away releases this viewfinder's use cases. HarmonyOS connects an ArkTS XComponent surface to the NDK camera and encodes captured images as JPEG. See the design and platform sources for the command protocol and resource ownership.

Compatibility and development

This checkout requires Rust 1.89 or newer and declares compatibility with Day 0.5 in Cargo.toml. The crate is consumed from Git, not crates.io. Its Day dependencies use https://github.com/daybrite/day.git without a branch, tag, or revision. Use the same source in your app and keep its Cargo.lock to record the resolved revisions. Mixing Day source URLs or refs can introduce duplicate framework crates and incompatible types.

For a local framework checkout, run day patch --local ../day from this repository (adjust the path when running from demo/). The demo depends on this crate by path and is a complete integration example.

Run cargo test for the host-side checks. From demo/, run day launch -p android-material --script dayscript/camera.yaml for the walkthrough. The separate dayscript/camera-capture.yaml takes a photo and needs the permission granted first, since Day scripts cannot dismiss the OS permission dialog: day launch -p android-material --grant camera --script dayscript/camera-capture.yaml marks it on the emulator before the app starts. CI launches that way on its Android leg and runs the capture script there on every push, beside the walkthrough on the iOS Simulator and the HarmonyOS emulator. Validate capture on a real iPhone and on the intended HarmonyOS hardware before relying on those paths.