Skip to content

fix(ios): preserve unsaved annotations when switching annotation tools (Fabric)#422

Merged
ignaciokit merged 1 commit into
masterfrom
ignacio/fix-ios-fabric-document-reload-on-prop-change
Jun 17, 2026
Merged

fix(ios): preserve unsaved annotations when switching annotation tools (Fabric)#422
ignaciokit merged 1 commit into
masterfrom
ignacio/fix-ios-fabric-document-reload-on-prop-change

Conversation

@ignaciokit

@ignaciokit ignaciokit commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Summary

On iOS with the New Architecture (Fabric), unsaved in-memory annotations are lost when switching annotation creation tools via a dynamic menuItemGrouping, unless document.save() is called first.

Root cause

NutrientView.updateProps:oldProps: (ios/Fabric/NutrientView.mm) runs with the full prop set on every re-render. The document block was guarded only by !newProps->document.empty():

if (!newProps->document.empty()) {
    [NutrientPropsDocumentHelper applyDocumentFromJSON:_document ...];
}

Since document is always set, this ran on every prop update. applyDocumentFromJSON assigns a freshly created PSPDFDocument to the controller (view.pdfController.document = ...), which reloads from disk. Any prop change — e.g. a menuItemGrouping recomputed from React state on each tool switch — therefore reloaded the document and discarded unsaved annotations. With autosave off they were never persisted, so they were lost.

Android does not reproduce this: PdfView.java guards the reload by document UID (!pdfFragment.getDocument().getUid().equals(document.getUid())). iOS Fabric had no equivalent guard.

Fix

Only re-apply the document when the path actually changed, mirroring Android's identity check:

BOOL documentChanged = (oldViewProps == nullptr) || (newProps->document != oldViewProps->document);
if (!newProps->document.empty() && documentChanged) { ... }

A dynamic-menuItemGrouping pattern now preserves unsaved annotations across tool switches with no client-side change and no document.save().

Testing

  • Added Catalog example Switch Annotation Tools with a dynamic/static grouping toggle.
  • Verified on the iOS simulator: with dynamic grouping ON (the reproducing pattern), drawing an ink annotation and switching to highlight/note without saving now preserves the annotation. Previously it vanished.
  • Android behavior unchanged (already guarded).

Tracking

@ignaciokit ignaciokit requested a review from pspdfkit-ci June 17, 2026 01:42
@ignaciokit ignaciokit force-pushed the ignacio/fix-ios-fabric-document-reload-on-prop-change branch from 962a55c to 809c04e Compare June 17, 2026 01:45
On the New Architecture, NutrientView.updateProps is invoked with the full
prop set on every re-render. The document block was guarded only by
`!newProps->document.empty()`, so since `document` is always set it ran on
every update — creating a fresh PSPDFDocument and reloading the controller
on any prop change (e.g. a dynamic `menuItemGrouping` driven by React state).
That reload discarded unsaved in-memory annotations.

Guard the re-apply on an actual document change
(`newProps->document != oldProps->document`), mirroring the document-identity
check already used on Android. Tool switching via a dynamic menuItemGrouping
now preserves unsaved annotations without calling document.save().

Adds a Catalog example (SwitchAnnotationTools) demonstrating the issue with a
dynamic/static grouping toggle.
@ignaciokit ignaciokit force-pushed the ignacio/fix-ios-fabric-document-reload-on-prop-change branch from 809c04e to 3bd53a1 Compare June 17, 2026 01:47

@erhard-pspdfkit erhard-pspdfkit left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@ignaciokit ignaciokit merged commit 7167687 into master Jun 17, 2026
@ignaciokit ignaciokit deleted the ignacio/fix-ios-fabric-document-reload-on-prop-change branch June 17, 2026 14:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants