nitro icon indicating copy to clipboard operation
nitro copied to clipboard

It's not possible to pass ArrayBuffer to Nitro View as prop

Open Nodonisko opened this issue 9 months ago • 8 comments

What's happening?

I want to create Nitro View with prop that accepts ArrayBuffer, but it will throw error in Swift component (did not tested Kotlin).

Image

Reproduceable Code

export interface QRCodeViewProps extends HybridViewProps {
  data: ArrayBuffer;
}

// later
const data = new ArrayBuffer(42);
<QRCode data={data} />

Swift component

  var data: ArrayBufferHolder = ArrayBufferHolder.allocate(size: 1) {
        didSet {
            let data = qrCodeData.toData(copyIfNeeded: true)
            // ....
        }
    }

Relevant log output

-

Device

iPhone Pro 16 (emulator)

Nitro Modules Version

0.24.1

Nitrogen Version

0.24.1

Can you reproduce this issue in the Nitro Example app here?

I didn't try (⚠️ your issue might get ignored & closed if you don't try this)

Additional information

Nodonisko avatar Mar 06 '25 09:03 Nodonisko

Valid 👍

Worth noting that a fair workaround would be to directly set the prop on the hybridRef (which avoids any React magic and just operates directly on the HybridObject):

export interface QRCodeViewProps extends HybridViewProps {
  data: ArrayBuffer
}

// later
const data = new ArrayBuffer(42);
<QRCode hybridRef={{ f: (ref) => { ref.data = data } }} />

mrousavy avatar Mar 06 '25 09:03 mrousavy

@mrousavy Thanks for workaround.

Can maybe explain little bit more why this doesn't produce thread hop? My understanding is Swift view code is running on different thread so it's ref methods should too.

Nodonisko avatar Mar 06 '25 10:03 Nodonisko

This is the only case where an implicit Thread hop occurs in Nitro. By default, everything is sync - except for HybridView prop setters.

Those go through React Native's view diffing mechanism, and this just schedules all prop changes on the UI Thread. That's why this happens.

mrousavy avatar Mar 06 '25 10:03 mrousavy

React Native schedules this function on the UI Thread: https://github.com/mrousavy/nitro/blob/60bedc58bf816c56b446d7871f41236c2ab25b4c/packages/react-native-nitro-image/nitrogen/generated/ios/c%2B%2B/views/HybridTestViewComponent.mm#L64-L99

mrousavy avatar Mar 06 '25 10:03 mrousavy

My understanding is Swift view code is running on different thread so it's ref methods should too.

Threading is only implicit because of React Native. Nitro itself does not do any thread hops for you. You are responsible for doing that. it's sync by default.

So that's why the hybridRef way works - it gives you a direct reference to the HybridObject. Anything you do on it, even setting props, will be fully sync - no thread hops involved.

Now that I think of it, you can even use this with your current approach;

export interface QRCodeViewProps extends HybridViewProps {
  data: ArrayBuffer
}

// later
const data = new ArrayBuffer(42);
<QRCode hybridRef={{ f: (ref) => { ref.data = data } }} />

No need for a method even.

It's just if you set a prop through React/JSX, React Native's implementation will do a Thread hop from JS to UI. That's just how it works.

mrousavy avatar Mar 06 '25 14:03 mrousavy

So do I understand correctly that if I use ref approach it will run that Swift component update on JS thread instead of UI thread?

Nodonisko avatar Mar 07 '25 08:03 Nodonisko

Yep 👍

mrousavy avatar Mar 07 '25 11:03 mrousavy

Thanks again for explanation, I didn't even know it's possible to run native components on JS thread.

Nodonisko avatar Mar 07 '25 14:03 Nodonisko

#736 solves this - if you create your ArrayBuffer on the native side, it's owning - and going through JS to native again won't lose that information from now on!

mrousavy avatar Jul 15 '25 14:07 mrousavy