Include the existing native sharedPhotoShareId in the SharedImage getSharedImage() result so the web layer can identify the current share. Add shareId to the native result dictionary and plugin response (NSNull on the no-image branch), and add shareId: string to the TypeScript SharedImageResult interface. This surfaces a value that was already produced natively (previously used only for logging) so application code can later pass it to markProcessed(). No behavior changes beyond exposing the value; Android untouched. Updates doc/share-target-ios-audit.md accordingly.
93 lines
3.8 KiB
Markdown
93 lines
3.8 KiB
Markdown
# iOS Share Target Audit
|
|
|
|
**Date:** 2026-06-30
|
|
**Platform:** iOS only (Android unchanged)
|
|
**Status:** Phase 1D complete — explicit share completion API added (not yet called)
|
|
|
|
## Purpose
|
|
|
|
This document tracks the native iOS share-target lifecycle and the
|
|
Capacitor API surface exposed to the web/TypeScript layer. It is updated as
|
|
each phase lands.
|
|
|
|
## Lifecycle Overview
|
|
|
|
1. The Share Extension (`ShareViewController.swift`) receives an image, writes
|
|
it to the App Group container, records metadata in App Group
|
|
`UserDefaults`, assigns a `shareId` (UUID), sets the `sharedPhotoReady`
|
|
flag, and opens the main app via `timesafari://`.
|
|
2. The main app reads the shared image through the `SharedImage` Capacitor
|
|
plugin.
|
|
3. The application layer processes the image, then (in a future phase) signals
|
|
explicit completion via `markProcessed(shareId)`.
|
|
|
|
## App Group Storage Keys
|
|
|
|
Stored in `UserDefaults(suiteName: "group.app.trentlarson.timesafari.share")`:
|
|
|
|
| Key | Set by | Meaning |
|
|
| --- | --- | --- |
|
|
| `sharedPhotoFileName` | Share Extension | Original display filename |
|
|
| `sharedPhotoFilePath` | Share Extension | On-disk filename in App Group container |
|
|
| `sharedPhotoShareId` | Share Extension | Current pending share identifier (UUID) |
|
|
| `sharedPhotoReady` | Share Extension | Flag indicating a share is ready |
|
|
| `sharedPhotoProcessedShareId` | Main app (`markProcessed`) | Last shareId explicitly marked processed (Phase 1D) |
|
|
|
|
## Capacitor API (`SharedImage` plugin)
|
|
|
|
The plugin is registered as `SharedImage` and is implemented in:
|
|
|
|
- Native iOS: `ios/App/App/SharedImagePlugin.swift` (delegates to
|
|
`ios/App/App/SharedImageUtility.swift`)
|
|
- Web stub: `src/plugins/SharedImagePlugin.web.ts`
|
|
- TypeScript definitions: `src/plugins/definitions.ts`
|
|
|
|
### `getSharedImage(): Promise<SharedImageResult | null>`
|
|
|
|
Returns the shared image as `{ base64, fileName, shareId }`, or `null` if none
|
|
exists. `shareId` is the native `sharedPhotoShareId` (UUID assigned by the
|
|
Share Extension); pass it to `markProcessed()` to signal explicit completion.
|
|
Read-only: native metadata and the file are left intact after retrieval
|
|
(Phase 1C).
|
|
|
|
> Note: On iOS the "no shared image" case resolves an object with `base64`,
|
|
> `fileName`, and `shareId` set to `null`; on web `getSharedImage()` resolves
|
|
> `null` directly.
|
|
|
|
### `hasSharedImage(): Promise<{ hasImage: boolean }>`
|
|
|
|
Returns whether a shared image file currently exists, without reading it.
|
|
|
|
### `markProcessed(options: { shareId: string }): Promise<{ success: boolean }>` — Phase 1D
|
|
|
|
Explicit completion signal from the application layer for a given share.
|
|
|
|
- **Input:** `shareId` — the identifier reported by `getSharedImage()`.
|
|
Must be a non-empty string; otherwise the call rejects.
|
|
- **Behavior (iOS):** Records `shareId` in App Group `UserDefaults` under
|
|
`sharedPhotoProcessedShareId` and resolves with `{ success: true }`.
|
|
- **Does NOT delete files:** The stored image file and share metadata are
|
|
intentionally left intact. File cleanup is deferred to a later phase.
|
|
- **Web:** No-op; resolves with `{ success: false }`.
|
|
- **Logging:** Emits `[ShareTarget] shareId=<id> marked processed` on success.
|
|
|
|
> Note (Phase 1D): `markProcessed` is implemented and exposed to TypeScript
|
|
> but is **not yet called** from application code. Wiring it into the
|
|
> processing flow is a later phase.
|
|
|
|
## Native Logging Reference
|
|
|
|
Relevant `[ShareTarget]` log lines emitted on iOS:
|
|
|
|
- `[ShareTarget] shareId=<id> retrieved`
|
|
- `[ShareTarget] shareId=<id> left intact after retrieval`
|
|
- `[ShareTarget] shareId=<id> marked processed` (Phase 1D)
|
|
|
|
## Out of Scope
|
|
|
|
- Android: no changes. The Android plugin
|
|
(`android/app/src/main/java/app/timesafari/sharedimage/SharedImagePlugin.java`)
|
|
does not implement `markProcessed`.
|
|
- File deletion / cleanup of processed shares.
|
|
- Calling `markProcessed` from application code.
|