Use Case: Mobile Rapid Object Capture

Created: Mon 07 Jul 2025 12:12:17 CEST
Updated: Thu 11 Jun 2026 22:13:27 CEST - Full rewrite: documented implemented MVP single-object rapid capture flow, write-behind queue semantics, and prefetch cache; moved unimplemented batch session ideas to Future section
Document Version: 2.0 - Implemented rapid capture flow
Security Classification: Internal Technical Documentation
Target Audience: iOS Developers, Frontend Developers, Backend Developers
Author: Paul Wisén

Overview

This document describes the mobile-optimized rapid object capture workflow in the Plings native iOS app. The implemented MVP flow is single-object rapid capture: scan an unknown tag, photograph the object, optionally name it, place it spatially, and return to the scanner — all in under one second of perceived wait time. The write-behind queue is what makes >10 objects per minute achievable: the next capture begins before the previous object has finished uploading.

User Story

As a mobile user, I want to quickly photograph and register objects in succession so that I can digitize my physical inventory without form-filling or waiting for each upload to complete.

Actors

  • Mobile User: Person using the native iOS app for object capture
  • System: Plings-iOS app + GraphQL API (api.plings.io)
  • Container: Spatial context (shelf, room, box) where objects are being captured

Preconditions

  • User is authenticated and has organization context
  • Camera and NFC permissions are granted
  • At least one Plings tag is available to scan
  • Network connectivity (online capture; offline queue support planned — see Future section)

Implemented Flow: Single-Object Rapid Capture

1. Scan Unknown Tag

The scanner is the home screen. When the user scans a tag that resolves to UNKNOWN_SPECIFIC or UNKNOWN_GENERAL, the create flow opens immediately with the tag pre-assigned. No separate “enter tag ID” step exists.

2. Camera Opens Immediately

After an unknown tag scan, the camera launches without any intermediate screen. The user photographs the object. This is the only mandatory capture; a name and spatial context can be added in the next step but are not required to proceed.

3. Optional Name + Server Auto-Name

The user can type a name or leave the field empty. If left empty, the server generates a name on createObject using the pattern:

[Organisation name] [Object class name] [Tag short code]

For example: Grön Teknik Cykel 4K7mX9. The auto-name is visible immediately once the object syncs.

4. Spatial Placement

The last-used container is pre-suggested and pre-selected so that repeated captures into the same space require zero interaction for placement. The user can:

  • Accept the suggestion — tap Save, placement is set.
  • Scan a container tag — tap “Scan container”, scan any tag that belongs to a container object; the container is resolved and pre-filled.
  • Pick from list — browse potentialContainers results for nearby or recently used containers.
  • Drag to place — the drag-to-place relationship picker lets the user choose the spatial relationship type (IN is preselected as the fast path; ON / LEFT_OF / RIGHT_OF / ABOVE / UNDER / NEXT_TO zones; ATTACHED_TO chip).

createObject is called with spatialParentId + spatialRelationshipType included in the same mutation. No separate spatial assignment step is needed for the common case.

5. Save Enqueues — Scanner Returns in <1 s

Tapping Save:

  1. Enqueues a createObject job in the write-behind queue (persisted to disk immediately).
  2. Enqueues an uploadObjectImages job (depends on the createObject job completing first — see queue semantics below).
  3. Returns the user to the scanner screen. The object appears in the “pending” state in any object list.

The user can scan the next tag while the previous object is still uploading.

Write-Behind Queue

The queue is the architectural feature that enables rapid successive capture. Its semantics:

Property Behaviour
Storage Disk-persisted (survives app restart and crashes)
Order FIFO — jobs execute in the order they were enqueued
Dependency uploadObjectImages is always enqueued after createObject for the same object. The executor does not start an image upload until the corresponding createObject has completed and returned an objectId. All object_images rows require an existing object.
Retry Exponential back-off on network/server errors. Transient failures are retried automatically.
Terminal failure Jobs that exhaust retries (e.g., API returns a permanent error) are moved to the needs-attention list — a visible section in the app’s inbox/notification area. The user sees which objects need attention and can retry or discard manually.
Concurrency The processor drains the queue sequentially (one job at a time) to avoid overwhelming the API and to preserve dependency order.

Read Cache with Neighbor Prefetch

When the user views an object, the cache preemptively fetches:

  • The object’s container (spatial parent) and its sibling objects (getSpatialChildren of the parent).
  • Low-resolution images of those siblings.

This means that navigating into a container after viewing one of its objects is typically instant. The cache is in-memory + disk-backed and has a configurable TTL. Stale cache entries are refreshed in the background when the user navigates to them (the known-tag fast path described in Tag Scanning Integration).

GraphQL Operations Used

Operation When called Notes
resolveIdentifier On every tag scan Mutation — see tag-scanning-integration.md
createObject Dequeued from write queue Includes tagInstanceKey, tagData, spatialParentId, spatialRelationshipType, autoGenerateName
uploadObjectImages Dequeued after createObject completes GraphQL multipart; requires objectId
potentialContainers Container picker Returns nearby / recently used containers
getSpatialChildren Prefetch on object view Populates cache for sibling navigation
getObjectDetailsComplete Object detail view Full object data
myOrganizations Session bootstrap Needed for auto-name context

createObjectWithTag does not exist in the API. Tag data is passed as fields on CreateObjectInput (tagInstanceKey + tagData).

User Experience Targets

  • >10 objects/minute: achieved by returning to the scanner in <1 s and queuing uploads async.
  • Minimal interaction per object: camera auto-focuses, server auto-names, last container pre-suggested.
  • No data loss on crash: disk-persistent queue ensures in-flight jobs survive app restart.
  • Clear error visibility: terminal failures surface in needs-attention list, not silently dropped.

Future: Batch Sessions

The items below are not implemented. No session API exists. They are preserved here as design intent for when batch workflows are built.

Batch Session Concept

A batch session would group multiple object captures under a shared session ID, enabling:

  • Progress tracking across captures (e.g., “12 of ~30 items scanned”)
  • Session recovery after interruption: if the app is force-closed mid-session, a “Resume session” banner appears on relaunch showing pending captures
  • Batch summary screen: review all created objects, rename, adjust placement, or discard before finalising
  • Bulk spatial assignment: set container once for the whole session

Bulk Operations (Future API)

# Not implemented — proposed
mutation CreateObjectsBatch($inputs: [CreateObjectInput!]!) {
  createObjectsBatch(inputs: $inputs) {
    created { id, name }
    failed { input, error }
  }
}

Offline Mode (Future)

Full offline capture (scan + photo without any network) would store jobs locally and sync when connectivity is restored. The current write-behind queue already handles intermittent connectivity; full offline support requires pre-loading organization context and class data before going offline.

AI-Assisted Naming (Future)

On-device vision model suggests an object name from the photo before the user confirms. The suggestion is editable and the server auto-name remains as fallback.