Skip to content

Fix: iOS node name in onPanEnd/onRotationEnd + null-safety (replicates upstream #31) - #4

Merged
iamEtornam merged 2 commits into
mainfrom
upstream-pr-31-ios-panrotate-nodename
Jul 8, 2026
Merged

Fix: iOS node name in onPanEnd/onRotationEnd + null-safety (replicates upstream #31)#4
iamEtornam merged 2 commits into
mainfrom
upstream-pr-31-ios-panrotate-nodename

Conversation

@iamEtornam

Copy link
Copy Markdown
Owner

Replicates upstream PR hlefe#31 by @nikkon404.

Change: iOS serializeLocalTransformation now guards against a nil node and uses non-optional transform/name; onPanEnd/onRotationEnd capture the node before clearing panningNode and only fire when non-nil. Also adds a matching defensive null check on Android (name?.let { ... }).

Original author: nikkon404. Upstream PR: hlefe#31

…acks

- Fixed serializeLocalTransformation to handle nil nodes gracefully
- Changed transform array from optional to non-optional Float array
- Changed node name from optional to non-optional String
- Store node reference before clearing panningNode to prevent nil access
- Only invoke onPanEnd/onRotationEnd if node exists

This fixes the issue where call.arguments["name"] was nil, causing
the Dart callback handler to fail silently in the catch block.
- Added defensive null check for name before creating transformMap
- Only invoke callback if name exists (consistent with iOS fix)
- Prevents potential crashes when name is null from nodeData["name"]

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces defensive checks in both the Android and iOS implementations of the AR Flutter plugin to prevent crashes and unexpected behaviors when nodes or their names are null during pan and rotation gestures. On Android, callbacks are only invoked if the node name is present. On iOS, the panning node reference is preserved before being cleared, and a guard statement is added to serializeLocalTransformation to return an empty dictionary if the node is nil. The reviewer suggested safely unwrapping the serialized transformation dictionary before invoking the platform channel methods on iOS to avoid sending null arguments to Flutter.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +634 to +636
if let node = nodeToSerialize {
DispatchQueue.main.async {self.objectManagerChannel.invokeMethod("onPanEnd", arguments: serializeLocalTransformation(node: node))}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

With serializeLocalTransformation updated to return an optional dictionary, we should safely unwrap the serialized transformation dictionary before invoking the platform channel method. This prevents sending nil arguments to Flutter, which would otherwise trigger a NoSuchMethodError when Dart tries to access keys on a null arguments object.

Suggested change
if let node = nodeToSerialize {
DispatchQueue.main.async {self.objectManagerChannel.invokeMethod("onPanEnd", arguments: serializeLocalTransformation(node: node))}
}
if let node = nodeToSerialize, let transformDict = serializeLocalTransformation(node: node) {
DispatchQueue.main.async {self.objectManagerChannel.invokeMethod("onPanEnd", arguments: transformDict)}
}

Comment on lines +700 to +702
if let node = nodeToSerialize {
DispatchQueue.main.async {self.objectManagerChannel.invokeMethod("onRotationEnd", arguments: serializeLocalTransformation(node: node))}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Similarly to onPanEnd, we should safely unwrap the serialized transformation dictionary returned by serializeLocalTransformation before invoking onRotationEnd to avoid sending nil arguments and causing a crash on the Flutter side.

Suggested change
if let node = nodeToSerialize {
DispatchQueue.main.async {self.objectManagerChannel.invokeMethod("onRotationEnd", arguments: serializeLocalTransformation(node: node))}
}
if let node = nodeToSerialize, let transformDict = serializeLocalTransformation(node: node) {
DispatchQueue.main.async {self.objectManagerChannel.invokeMethod("onRotationEnd", arguments: transformDict)}
}

@iamEtornam

Copy link
Copy Markdown
Owner Author

Review — VERDICT: ✅ LGTM (with nit)

Replicates upstream hlefe#31. Swift+Kotlin; not compilable by flutter analyze here — reviewed by static read.

  • iOS fix correct (IosARView.swift:629-636 pan, :695-702 rotate): captures nodeToSerialize = panningNode before the panningNode = nil cleanup and only invokes the channel if let node. Fixes the nil-name bug and a latent async-read-after-nil bug.
  • serializeLocalTransformation (Serializers.swift:42-55) now guards non-nil node → transform is always a clean [Float], matching the Dart MatrixConverter().fromJson(...['transform']).
  • Android consistent (ArView.kt): name?.let { ... } guarantees a non-null name key, exactly what Dart's as String expects. Payload shape identical to iOS.
  • No regression: when node is nil the callback simply doesn't fire (previously fired with nil name that Dart caught & discarded) — net behavior unchanged, minus a spurious error log.
  • 🔸 Nit: iOS guard checks node != nil but not node.name != nil; safe in practice because panningNode only comes from nearestParentWithNameStart(...,"[#") (always named).

Safe to merge.

@iamEtornam
iamEtornam merged commit 85ed70a into main Jul 8, 2026
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