diff --git a/bun.lock b/bun.lock index 67660818..a54cd161 100644 --- a/bun.lock +++ b/bun.lock @@ -29,7 +29,7 @@ "react-native": "0.81.0", "react-native-fast-image": "^8.6.3", "react-native-nitro-image": "../packages/react-native-nitro-image", - "react-native-nitro-modules": "0.32.1", + "react-native-nitro-modules": "0.33.0", "react-native-nitro-web-image": "../packages/react-native-nitro-web-image", "react-native-safe-area-context": "^5.6.0", "react-native-screens": "^4.14.1", @@ -54,10 +54,10 @@ "devDependencies": { "@biomejs/biome": "2.2.6", "@types/react": "^19.0.6", - "nitrogen": "0.32.1", + "nitrogen": "0.33.0", "react": "19.1.0", "react-native": "0.81.0", - "react-native-nitro-modules": "0.32.1", + "react-native-nitro-modules": "0.33.0", "typescript": "5.8.3", }, "peerDependencies": { @@ -72,11 +72,11 @@ "devDependencies": { "@biomejs/biome": "2.2.6", "@types/react": "^19.0.6", - "nitrogen": "0.32.1", + "nitrogen": "0.33.0", "react": "19.1.0", "react-native": "0.81.0", "react-native-nitro-image": "../react-native-nitro-image", - "react-native-nitro-modules": "0.32.1", + "react-native-nitro-modules": "0.33.0", "typescript": "5.8.3", }, "peerDependencies": { @@ -1284,7 +1284,7 @@ "new-github-release-url": ["new-github-release-url@2.0.0", "", { "dependencies": { "type-fest": "^2.5.1" } }, ""], - "nitrogen": ["nitrogen@0.32.1", "", { "dependencies": { "chalk": "^5.3.0", "react-native-nitro-modules": "^0.32.1", "ts-morph": "^27.0.0", "yargs": "^18.0.0", "zod": "^4.0.5" }, "bin": { "nitrogen": "lib/index.js" } }, "sha512-/bMqBXfq+ICboH34pHffyp1eftSAb+VS5QEne94gRwcNawwPI3TqpmDowpM7/BlKtwXgl3mLucQx4sWjVLYehQ=="], + "nitrogen": ["nitrogen@0.33.0", "", { "dependencies": { "chalk": "^5.3.0", "react-native-nitro-modules": "^0.33.0", "ts-morph": "^27.0.0", "yargs": "^18.0.0", "zod": "^4.0.5" }, "bin": { "nitrogen": "lib/index.js" } }, "sha512-/OakCMfovMILJRuwv8zsyNZkNdHJpusXkz5NahmdfrPlf2A/oFENOZFNjU8wA7iSaopp7hyI4bUINXx3xc6ybw=="], "nocache": ["nocache@3.0.4", "", {}, ""], @@ -1440,7 +1440,7 @@ "react-native-nitro-image": ["react-native-nitro-image@workspace:packages/react-native-nitro-image"], - "react-native-nitro-modules": ["react-native-nitro-modules@0.32.1", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-V+Vy76e4fxRxgVGu5Uh3cBPvuFQW8fM1OUKk1mqEA/JawjhX+hxHtBhpfuvNjV0BnV/uXCIg8/eK+rTpB6tqFg=="], + "react-native-nitro-modules": ["react-native-nitro-modules@0.33.0", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-LwY4l8hn8SbLFpD9Od3wXosSdTHcSLYP3sH6SPSKNY5UOgOcFK6Mw0eG0QjrWIGtnb6igKbbQgD31N4o7Ptv6Q=="], "react-native-nitro-web-image": ["react-native-nitro-web-image@workspace:packages/react-native-nitro-web-image"], diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index e6fda856..d3675e3d 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -50,7 +50,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - NitroModules (0.32.1): + - NitroModules (0.33.0): - boost - DoubleConversion - fast_float @@ -2740,9 +2740,9 @@ SPEC CHECKSUMS: glog: 5683914934d5b6e4240e497e0f4a3b42d1854183 hermes-engine: e7491a2038f2618c8cd444ed411a6deb350a3742 libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8 - NitroImage: 5ac8b2795c7106d95064d07208ded5b5a3287e62 - NitroModules: e28ee6e0ad1246a20db02fe73ba994225fb503ff - NitroWebImage: 3421f3c6c9e8a8dcac3bce72faa9e5c53debdbe4 + NitroImage: e247502478146ffcb0727d37fbcee63e150188a0 + NitroModules: a375db5ca93f5f8a9b8fdd81bf3b8e94900b183d + NitroWebImage: 5e1dafa038c9f31da5e01c1909bc833d727a29d0 RCT-Folly: 59ec0ac1f2f39672a0c6e6cecdd39383b764646f RCTDeprecation: 0735ab4f6b3ec93a7f98187b5da74d7916e2cf4c RCTRequired: 8fcc7801bfc433072287b0f24a662e2816e89d0c diff --git a/example/package.json b/example/package.json index ef7c0263..699e4029 100644 --- a/example/package.json +++ b/example/package.json @@ -19,7 +19,7 @@ "react-native-fast-image": "^8.6.3", "react-native-nitro-image": "../packages/react-native-nitro-image", "react-native-nitro-web-image": "../packages/react-native-nitro-web-image", - "react-native-nitro-modules": "0.32.1", + "react-native-nitro-modules": "0.33.0", "react-native-safe-area-context": "^5.6.0", "react-native-screens": "^4.14.1" }, diff --git a/packages/react-native-nitro-image/android/src/main/java/com/margelo/nitro/image/ArrayBuffer+copyIfNotOwner.kt b/packages/react-native-nitro-image/android/src/main/java/com/margelo/nitro/image/ArrayBuffer+copyIfNotOwner.kt deleted file mode 100644 index 58513e96..00000000 --- a/packages/react-native-nitro-image/android/src/main/java/com/margelo/nitro/image/ArrayBuffer+copyIfNotOwner.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.margelo.nitro.image - -import com.margelo.nitro.core.ArrayBuffer - -fun ArrayBuffer.copyIfNotOwner(): ArrayBuffer { - if (!this.isOwner) { - return ArrayBuffer.copy(this) - } - return this -} diff --git a/packages/react-native-nitro-image/android/src/main/java/com/margelo/nitro/image/ArrayBuffer+toByteArray.kt b/packages/react-native-nitro-image/android/src/main/java/com/margelo/nitro/image/ArrayBuffer+toByteArray.kt deleted file mode 100644 index 9f912379..00000000 --- a/packages/react-native-nitro-image/android/src/main/java/com/margelo/nitro/image/ArrayBuffer+toByteArray.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.margelo.nitro.image - -import com.margelo.nitro.core.ArrayBuffer -import java.nio.ByteBuffer - -fun ArrayBuffer.toByteArray(): ByteArray { - val buffer = this.getBuffer(false) - if (buffer.hasArray()) { - // It's a CPU-backed array - we can return this directly - val array = buffer.array() - if (array.size == this.size) { - // The CPU-backed array is exactly the view we have in our ArrayBuffer. - // Return as is! - return array - } - // we had a CPU-backed array, but it's size differs from our ArrayBuffer size. - // This might be because the ArrayBuffer has a smaller view of the data, so we need - // to resort back to a good ol' copy. - } - // It's not a CPU-backed array (e.g. HardwareBuffer) - we need to copy to the CPU - val copy = ByteBuffer.allocate(buffer.capacity()) - copy.put(buffer) - return copy.array() -} diff --git a/packages/react-native-nitro-image/android/src/main/java/com/margelo/nitro/image/HybridImageFactory.kt b/packages/react-native-nitro-image/android/src/main/java/com/margelo/nitro/image/HybridImageFactory.kt index bfe3c91a..30fa45a4 100644 --- a/packages/react-native-nitro-image/android/src/main/java/com/margelo/nitro/image/HybridImageFactory.kt +++ b/packages/react-native-nitro-image/android/src/main/java/com/margelo/nitro/image/HybridImageFactory.kt @@ -67,7 +67,7 @@ class HybridImageFactory: HybridImageFactorySpec() { return HybridImage(bitmap) } override fun loadFromRawPixelDataAsync(data: RawPixelData, allowGpu: Boolean?): Promise { - val bufferCopy = data.buffer.copyIfNotOwner() + val bufferCopy = data.buffer.asOwning() val dataCopy = RawPixelData(bufferCopy, data.width, data.height, data.pixelFormat) return Promise.async { loadFromRawPixelData(dataCopy, allowGpu) } } @@ -111,13 +111,12 @@ class HybridImageFactory: HybridImageFactorySpec() { } override fun loadFromThumbHash(thumbhash: ArrayBuffer): HybridImageSpec { - // copyIfNeeded can be false since we are running this synchronously val bytes = thumbhash.toByteArray() return loadFromThumbHash(bytes) } override fun loadFromThumbHashAsync(thumbhash: ArrayBuffer): Promise { - // copyIfNeeded needs to be true since we switch threads + // We need to copy before jumping Threads val bytes = thumbhash.toByteArray() return Promise.async { loadFromThumbHash(bytes) } } diff --git a/packages/react-native-nitro-image/android/src/main/java/com/margelo/nitro/image/HybridImageView.kt b/packages/react-native-nitro-image/android/src/main/java/com/margelo/nitro/image/HybridImageView.kt index dc189a4e..8fa4ee76 100644 --- a/packages/react-native-nitro-image/android/src/main/java/com/margelo/nitro/image/HybridImageView.kt +++ b/packages/react-native-nitro-image/android/src/main/java/com/margelo/nitro/image/HybridImageView.kt @@ -7,13 +7,14 @@ import android.widget.ImageView import androidx.annotation.Keep import androidx.core.view.isVisible import com.facebook.common.internal.DoNotStrip +import com.margelo.nitro.views.RecyclableView import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @DoNotStrip @Keep -class HybridImageView(context: Context): HybridNitroImageViewSpec() { +class HybridImageView(context: Context): HybridNitroImageViewSpec(), RecyclableView { companion object { private const val TAG = "HybridImageView" } @@ -48,6 +49,11 @@ class HybridImageView(context: Context): HybridNitroImageViewSpec() { field = value } + override fun prepareForRecycle() { + onDisappear() + imageView.setImageBitmap(null) + } + private fun updateResizeMode() { imageView.scaleType = when (resizeMode) { ResizeMode.COVER -> ImageView.ScaleType.CENTER_CROP diff --git a/packages/react-native-nitro-image/ios/CustomImageView.swift b/packages/react-native-nitro-image/ios/CustomImageView.swift new file mode 100644 index 00000000..08120a16 --- /dev/null +++ b/packages/react-native-nitro-image/ios/CustomImageView.swift @@ -0,0 +1,41 @@ +// +// CustomImageView.swift +// react-native-nitro-image +// +// Created by Marc Rousavy on 12.01.26. +// + +import Foundation +import UIKit + +internal protocol ViewLifecycleDelegate: AnyObject { + func willShow() + func willHide() +} + +internal class CustomImageView: UIImageView { + internal weak var delegate: (any ViewLifecycleDelegate)? = nil { + didSet { + onVisibilityChanged(isVisible: self.isVisible) + } + } + + init() { + super.init(image: nil) + } + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func willMove(toSuperview newSuperview: UIView?) { + super.willMove(toSuperview: newSuperview) + onVisibilityChanged(isVisible: newSuperview != nil) + } + private func onVisibilityChanged(isVisible: Bool) { + if isVisible { + delegate?.willShow() + } else { + delegate?.willHide() + } + } +} diff --git a/packages/react-native-nitro-image/ios/CGImage+getPixelFormat.swift b/packages/react-native-nitro-image/ios/Extensions/CGImage+getPixelFormat.swift similarity index 100% rename from packages/react-native-nitro-image/ios/CGImage+getPixelFormat.swift rename to packages/react-native-nitro-image/ios/Extensions/CGImage+getPixelFormat.swift diff --git a/packages/react-native-nitro-image/ios/Color+toUIColor.swift b/packages/react-native-nitro-image/ios/Extensions/Color+toUIColor.swift similarity index 100% rename from packages/react-native-nitro-image/ios/Color+toUIColor.swift rename to packages/react-native-nitro-image/ios/Extensions/Color+toUIColor.swift diff --git a/packages/react-native-nitro-image/ios/ImageFormat+toUTType.swift b/packages/react-native-nitro-image/ios/Extensions/ImageFormat+toUTType.swift similarity index 100% rename from packages/react-native-nitro-image/ios/ImageFormat+toUTType.swift rename to packages/react-native-nitro-image/ios/Extensions/ImageFormat+toUTType.swift diff --git a/packages/react-native-nitro-image/ios/UIImage+fromRawPixelData.swift b/packages/react-native-nitro-image/ios/Extensions/UIImage+fromRawPixelData.swift similarity index 100% rename from packages/react-native-nitro-image/ios/UIImage+fromRawPixelData.swift rename to packages/react-native-nitro-image/ios/Extensions/UIImage+fromRawPixelData.swift diff --git a/packages/react-native-nitro-image/ios/UIImage+getData.swift b/packages/react-native-nitro-image/ios/Extensions/UIImage+getData.swift similarity index 100% rename from packages/react-native-nitro-image/ios/UIImage+getData.swift rename to packages/react-native-nitro-image/ios/Extensions/UIImage+getData.swift diff --git a/packages/react-native-nitro-image/ios/UIImage+memorySize.swift b/packages/react-native-nitro-image/ios/Extensions/UIImage+memorySize.swift similarity index 100% rename from packages/react-native-nitro-image/ios/UIImage+memorySize.swift rename to packages/react-native-nitro-image/ios/Extensions/UIImage+memorySize.swift diff --git a/packages/react-native-nitro-image/ios/UIImage+toEncodedImageData.swift b/packages/react-native-nitro-image/ios/Extensions/UIImage+toEncodedImageData.swift similarity index 100% rename from packages/react-native-nitro-image/ios/UIImage+toEncodedImageData.swift rename to packages/react-native-nitro-image/ios/Extensions/UIImage+toEncodedImageData.swift diff --git a/packages/react-native-nitro-image/ios/UIImage+toRawPixelData.swift b/packages/react-native-nitro-image/ios/Extensions/UIImage+toRawPixelData.swift similarity index 100% rename from packages/react-native-nitro-image/ios/UIImage+toRawPixelData.swift rename to packages/react-native-nitro-image/ios/Extensions/UIImage+toRawPixelData.swift diff --git a/packages/react-native-nitro-image/ios/UIImageOrientation+fromDegrees.swift b/packages/react-native-nitro-image/ios/Extensions/UIImageOrientation+fromDegrees.swift similarity index 100% rename from packages/react-native-nitro-image/ios/UIImageOrientation+fromDegrees.swift rename to packages/react-native-nitro-image/ios/Extensions/UIImageOrientation+fromDegrees.swift diff --git a/packages/react-native-nitro-image/ios/Extensions/UIView+isVisible.swift b/packages/react-native-nitro-image/ios/Extensions/UIView+isVisible.swift new file mode 100644 index 00000000..03cc15c1 --- /dev/null +++ b/packages/react-native-nitro-image/ios/Extensions/UIView+isVisible.swift @@ -0,0 +1,14 @@ +// +// UIView+isVisible.swift +// react-native-nitro-image +// +// Created by Marc Rousavy on 12.01.26. +// + +import UIKit + +extension UIView { + var isVisible: Bool { + return superview != nil + } +} diff --git a/packages/react-native-nitro-image/ios/HybridImageView.swift b/packages/react-native-nitro-image/ios/HybridImageView.swift index beb9da81..9ab77732 100644 --- a/packages/react-native-nitro-image/ios/HybridImageView.swift +++ b/packages/react-native-nitro-image/ios/HybridImageView.swift @@ -9,55 +9,13 @@ import Foundation import UIKit import NitroModules -fileprivate protocol ViewLifecycleDelegate: AnyObject { - func willShow() - func willHide() -} - -class CustomImageView: UIImageView { - fileprivate weak var delegate: ViewLifecycleDelegate? = nil { - didSet { - onVisibilityChanged(isVisible: superview != nil) - } - } - - init() { - super.init(image: nil) - } - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func willMove(toSuperview newSuperview: UIView?) { - super.willMove(toSuperview: newSuperview) - onVisibilityChanged(isVisible: newSuperview != nil) - } - private func onVisibilityChanged(isVisible: Bool) { - if isVisible { - delegate?.willShow() - } else { - delegate?.willHide() - } - } -} - -fileprivate extension UIView { - var isVisible: Bool { - return superview != nil - } -} - -class HybridImageView: HybridNitroImageViewSpec, NativeImageView { - let imageView: UIImageView - let view: UIView +class HybridImageView: HybridNitroImageViewSpec { + let view = CustomImageView() private var resetImageBeforeLoad = false override init() { - let imageView = CustomImageView() - self.imageView = imageView - self.view = imageView super.init() - imageView.delegate = self + view.delegate = self } var resizeMode: ResizeMode? { @@ -84,13 +42,13 @@ class HybridImageView: HybridNitroImageViewSpec, NativeImageView { let mode = resizeMode ?? .cover switch mode { case .cover: - imageView.contentMode = .scaleAspectFill + view.contentMode = .scaleAspectFill case .contain: - imageView.contentMode = .scaleAspectFit + view.contentMode = .scaleAspectFit case .stretch: - imageView.contentMode = .scaleToFill + view.contentMode = .scaleToFill case .center: - imageView.contentMode = .center + view.contentMode = .center } } @@ -101,19 +59,19 @@ class HybridImageView: HybridNitroImageViewSpec, NativeImageView { guard let image = hybridImageSpec as? NativeImage else { fatalError("Can't set `image` to a type that doesn't conform to `NativeImage`!") } - imageView.image = image.uiImage + view.image = image.uiImage case .second: // Image Loader - trigger a load or drop didSetImageLoader() case nil: // No Image - imageView.image = nil + view.image = nil } } private func didSetImageLoader() { // An ImageLoader was set - trigger an update (load or drop) - if imageView.isVisible { + if view.isVisible { willShow() } else { willHide() @@ -121,6 +79,11 @@ class HybridImageView: HybridNitroImageViewSpec, NativeImageView { } } +// Conform to `NativeImageView` so third-party consumers can cast it +extension HybridImageView: NativeImageView { + var imageView: UIImageView { view } +} + // Implementation for "asynchronously" loading Images using ImageLoader extension HybridImageView: ViewLifecycleDelegate { private var imageLoader: HybridImageLoaderSpec? { @@ -131,7 +94,7 @@ extension HybridImageView: ViewLifecycleDelegate { func willShow() { guard let imageLoader else { return } if resetImageBeforeLoad { - imageView.image = nil + view.image = nil resetImageBeforeLoad = false } try? imageLoader.requestImage(forView: self) @@ -143,3 +106,11 @@ extension HybridImageView: ViewLifecycleDelegate { } } +// Implementation to allow view recycling +extension HybridImageView: RecyclableView { + func prepareForRecycle() { + willHide() + view.image = nil + } +} + diff --git a/packages/react-native-nitro-image/nitrogen/generated/android/kotlin/com/margelo/nitro/image/views/HybridNitroImageViewManager.kt b/packages/react-native-nitro-image/nitrogen/generated/android/kotlin/com/margelo/nitro/image/views/HybridNitroImageViewManager.kt index 01b5b67e..794773c5 100644 --- a/packages/react-native-nitro-image/nitrogen/generated/android/kotlin/com/margelo/nitro/image/views/HybridNitroImageViewManager.kt +++ b/packages/react-native-nitro-image/nitrogen/generated/android/kotlin/com/margelo/nitro/image/views/HybridNitroImageViewManager.kt @@ -12,13 +12,20 @@ import com.facebook.react.uimanager.ReactStylesDiffMap import com.facebook.react.uimanager.SimpleViewManager import com.facebook.react.uimanager.StateWrapper import com.facebook.react.uimanager.ThemedReactContext +import com.margelo.nitro.R.id.associated_hybrid_view_tag +import com.margelo.nitro.views.RecyclableView import com.margelo.nitro.image.* /** * Represents the React Native `ViewManager` for the "NitroImageView" Nitro HybridView. */ open class HybridNitroImageViewManager: SimpleViewManager() { - private val views = hashMapOf() + init { + if (RecyclableView::class.java.isAssignableFrom(HybridImageView::class.java)) { + // Enable view recycling + super.setupViewRecycling() + } + } override fun getName(): String { return "NitroImageView" @@ -27,17 +34,13 @@ open class HybridNitroImageViewManager: SimpleViewManager() { override fun createViewInstance(reactContext: ThemedReactContext): View { val hybridView = HybridImageView(reactContext) val view = hybridView.view - views[view] = hybridView + view.setTag(associated_hybrid_view_tag, hybridView) return view } - override fun onDropViewInstance(view: View) { - super.onDropViewInstance(view) - views.remove(view) - } - override fun updateState(view: View, props: ReactStylesDiffMap, stateWrapper: StateWrapper): Any? { - val hybridView = views[view] ?: throw Error("Couldn't find view $view in local views table!") + val hybridView = view.getTag(associated_hybrid_view_tag) as? HybridImageView + ?: throw Error("Couldn't find view $view in local views table!") // 1. Update each prop individually hybridView.beforeUpdate() @@ -48,9 +51,20 @@ open class HybridNitroImageViewManager: SimpleViewManager() { return super.updateState(view, props, stateWrapper) } - protected override fun setupViewRecycling() { - // TODO: Recycling should be controllable by the user. WIP, but disabled for now. - // By not calling `super.setupViewRecycling()`, we effectively - // disable view recycling for now. + protected override fun prepareToRecycleView(reactContext: ThemedReactContext, view: View): View? { + super.prepareToRecycleView(reactContext, view) + val hybridView = view.getTag(associated_hybrid_view_tag) as? HybridImageView + ?: return null + + @Suppress("USELESS_IS_CHECK") + if (hybridView is RecyclableView) { + // Recycle in it's implementation + hybridView.prepareForRecycle() + + // Maybe update the view if it changed + return hybridView.view + } else { + return null + } } } diff --git a/packages/react-native-nitro-image/nitrogen/generated/ios/NitroImageAutolinking.mm b/packages/react-native-nitro-image/nitrogen/generated/ios/NitroImageAutolinking.mm index 5eec6ef7..7d8eef59 100644 --- a/packages/react-native-nitro-image/nitrogen/generated/ios/NitroImageAutolinking.mm +++ b/packages/react-native-nitro-image/nitrogen/generated/ios/NitroImageAutolinking.mm @@ -27,28 +27,28 @@ + (void) load { HybridObjectRegistry::registerHybridObjectConstructor( "ImageFactory", []() -> std::shared_ptr { - std::shared_ptr hybridObject = NitroImage::NitroImageAutolinking::createImageFactory(); + std::shared_ptr hybridObject = NitroImage::NitroImageAutolinking::ImageFactory::create(); return hybridObject; } ); HybridObjectRegistry::registerHybridObjectConstructor( "ImageLoaderFactory", []() -> std::shared_ptr { - std::shared_ptr hybridObject = NitroImage::NitroImageAutolinking::createImageLoaderFactory(); + std::shared_ptr hybridObject = NitroImage::NitroImageAutolinking::ImageLoaderFactory::create(); return hybridObject; } ); HybridObjectRegistry::registerHybridObjectConstructor( "ImageUtils", []() -> std::shared_ptr { - std::shared_ptr hybridObject = NitroImage::NitroImageAutolinking::createImageUtils(); + std::shared_ptr hybridObject = NitroImage::NitroImageAutolinking::ImageUtils::create(); return hybridObject; } ); HybridObjectRegistry::registerHybridObjectConstructor( "NitroImageView", []() -> std::shared_ptr { - std::shared_ptr hybridObject = NitroImage::NitroImageAutolinking::createNitroImageView(); + std::shared_ptr hybridObject = NitroImage::NitroImageAutolinking::NitroImageView::create(); return hybridObject; } ); diff --git a/packages/react-native-nitro-image/nitrogen/generated/ios/NitroImageAutolinking.swift b/packages/react-native-nitro-image/nitrogen/generated/ios/NitroImageAutolinking.swift index 7f3689b1..9b1eeaf4 100644 --- a/packages/react-native-nitro-image/nitrogen/generated/ios/NitroImageAutolinking.swift +++ b/packages/react-native-nitro-image/nitrogen/generated/ios/NitroImageAutolinking.swift @@ -5,66 +5,79 @@ /// Copyright © Marc Rousavy @ Margelo /// +import NitroModules + +// TODO: Use empty enums once Swift supports exporting them as namespaces +// See: https://github.com/swiftlang/swift/pull/83616 public final class NitroImageAutolinking { public typealias bridge = margelo.nitro.image.bridge.swift - /** - * Creates an instance of a Swift class that implements `HybridImageFactorySpec`, - * and wraps it in a Swift class that can directly interop with C++ (`HybridImageFactorySpec_cxx`) - * - * This is generated by Nitrogen and will initialize the class specified - * in the `"autolinking"` property of `nitro.json` (in this case, `HybridImageFactory`). - */ - public static func createImageFactory() -> bridge.std__shared_ptr_HybridImageFactorySpec_ { - let hybridObject = HybridImageFactory() - return { () -> bridge.std__shared_ptr_HybridImageFactorySpec_ in - let __cxxWrapped = hybridObject.getCxxWrapper() - return __cxxWrapped.getCxxPart() - }() + private protocol AutolinkedClass { + associatedtype T + /** + * Creates an instance of the Swift class that implements the HybridObject's spec, + * and wraps it in a Swift class that can directly interop with C++. + * + * This is generated by Nitrogen and will initialize the class specified + * in the `"autolinking"` property of `nitro.json`. + */ + static func create() -> T + /** + * Returns whether this concrete implementation is also + * conforming to the `RecyclableView` protocol, or not. + */ + static var isRecyclableHybridView: Bool { get } + } + + public final class ImageFactory: AutolinkedClass { + public static func create() -> bridge.std__shared_ptr_HybridImageFactorySpec_ { + let hybridObject = HybridImageFactory() + return { () -> bridge.std__shared_ptr_HybridImageFactorySpec_ in + let __cxxWrapped = hybridObject.getCxxWrapper() + return __cxxWrapped.getCxxPart() + }() + } + public static var isRecyclableHybridView: Bool { + return HybridImageFactory.self is any RecyclableView.Type + } } - /** - * Creates an instance of a Swift class that implements `HybridImageLoaderFactorySpec`, - * and wraps it in a Swift class that can directly interop with C++ (`HybridImageLoaderFactorySpec_cxx`) - * - * This is generated by Nitrogen and will initialize the class specified - * in the `"autolinking"` property of `nitro.json` (in this case, `HybridImageLoaderFactory`). - */ - public static func createImageLoaderFactory() -> bridge.std__shared_ptr_HybridImageLoaderFactorySpec_ { - let hybridObject = HybridImageLoaderFactory() - return { () -> bridge.std__shared_ptr_HybridImageLoaderFactorySpec_ in - let __cxxWrapped = hybridObject.getCxxWrapper() - return __cxxWrapped.getCxxPart() - }() + public final class ImageLoaderFactory: AutolinkedClass { + public static func create() -> bridge.std__shared_ptr_HybridImageLoaderFactorySpec_ { + let hybridObject = HybridImageLoaderFactory() + return { () -> bridge.std__shared_ptr_HybridImageLoaderFactorySpec_ in + let __cxxWrapped = hybridObject.getCxxWrapper() + return __cxxWrapped.getCxxPart() + }() + } + public static var isRecyclableHybridView: Bool { + return HybridImageLoaderFactory.self is any RecyclableView.Type + } } - /** - * Creates an instance of a Swift class that implements `HybridImageUtilsSpec`, - * and wraps it in a Swift class that can directly interop with C++ (`HybridImageUtilsSpec_cxx`) - * - * This is generated by Nitrogen and will initialize the class specified - * in the `"autolinking"` property of `nitro.json` (in this case, `HybridImageUtils`). - */ - public static func createImageUtils() -> bridge.std__shared_ptr_HybridImageUtilsSpec_ { - let hybridObject = HybridImageUtils() - return { () -> bridge.std__shared_ptr_HybridImageUtilsSpec_ in - let __cxxWrapped = hybridObject.getCxxWrapper() - return __cxxWrapped.getCxxPart() - }() + public final class ImageUtils: AutolinkedClass { + public static func create() -> bridge.std__shared_ptr_HybridImageUtilsSpec_ { + let hybridObject = HybridImageUtils() + return { () -> bridge.std__shared_ptr_HybridImageUtilsSpec_ in + let __cxxWrapped = hybridObject.getCxxWrapper() + return __cxxWrapped.getCxxPart() + }() + } + public static var isRecyclableHybridView: Bool { + return HybridImageUtils.self is any RecyclableView.Type + } } - /** - * Creates an instance of a Swift class that implements `HybridNitroImageViewSpec`, - * and wraps it in a Swift class that can directly interop with C++ (`HybridNitroImageViewSpec_cxx`) - * - * This is generated by Nitrogen and will initialize the class specified - * in the `"autolinking"` property of `nitro.json` (in this case, `HybridImageView`). - */ - public static func createNitroImageView() -> bridge.std__shared_ptr_HybridNitroImageViewSpec_ { - let hybridObject = HybridImageView() - return { () -> bridge.std__shared_ptr_HybridNitroImageViewSpec_ in - let __cxxWrapped = hybridObject.getCxxWrapper() - return __cxxWrapped.getCxxPart() - }() + public final class NitroImageView: AutolinkedClass { + public static func create() -> bridge.std__shared_ptr_HybridNitroImageViewSpec_ { + let hybridObject = HybridImageView() + return { () -> bridge.std__shared_ptr_HybridNitroImageViewSpec_ in + let __cxxWrapped = hybridObject.getCxxWrapper() + return __cxxWrapped.getCxxPart() + }() + } + public static var isRecyclableHybridView: Bool { + return HybridImageView.self is any RecyclableView.Type + } } } diff --git a/packages/react-native-nitro-image/nitrogen/generated/ios/c++/views/HybridNitroImageViewComponent.mm b/packages/react-native-nitro-image/nitrogen/generated/ios/c++/views/HybridNitroImageViewComponent.mm index 63996cdc..43a74bb3 100644 --- a/packages/react-native-nitro-image/nitrogen/generated/ios/c++/views/HybridNitroImageViewComponent.mm +++ b/packages/react-native-nitro-image/nitrogen/generated/ios/c++/views/HybridNitroImageViewComponent.mm @@ -41,14 +41,9 @@ + (void) load { return react::concreteComponentDescriptorProvider(); } -+ (BOOL)shouldBeRecycled { - // TODO: Recycling should be controllable by the user. WIP, but disabled for now. - return NO; -} - - (instancetype) init { if (self = [super init]) { - std::shared_ptr hybridView = NitroImage::NitroImageAutolinking::createNitroImageView(); + std::shared_ptr hybridView = NitroImage::NitroImageAutolinking::NitroImageView::create(); _hybridView = std::dynamic_pointer_cast(hybridView); [self updateView]; } @@ -109,4 +104,14 @@ - (void) updateProps:(const std::shared_ptr&)props [super updateProps:props oldProps:oldProps]; } ++ (BOOL)shouldBeRecycled { + return NitroImage::NitroImageAutolinking::NitroImageView::isRecyclableHybridView(); +} + +- (void)prepareForRecycle { + [super prepareForRecycle]; + NitroImage::HybridNitroImageViewSpec_cxx& swiftPart = _hybridView->getSwiftPart(); + swiftPart.maybePrepareForRecycle(); +} + @end diff --git a/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridImageFactorySpec.swift b/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridImageFactorySpec.swift index 7155990d..bb6a4434 100644 --- a/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridImageFactorySpec.swift +++ b/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridImageFactorySpec.swift @@ -42,14 +42,14 @@ open class HybridImageFactorySpec_base { public init() { } public func getCxxWrapper() -> HybridImageFactorySpec_cxx { #if DEBUG - guard self is HybridImageFactorySpec else { + guard self is any HybridImageFactorySpec else { fatalError("`self` is not a `HybridImageFactorySpec`! Did you accidentally inherit from `HybridImageFactorySpec_base` instead of `HybridImageFactorySpec`?") } #endif if let cxxWrapper = self.cxxWrapper { return cxxWrapper } else { - let cxxWrapper = HybridImageFactorySpec_cxx(self as! HybridImageFactorySpec) + let cxxWrapper = HybridImageFactorySpec_cxx(self as! any HybridImageFactorySpec) self.cxxWrapper = cxxWrapper return cxxWrapper } diff --git a/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridImageLoaderFactorySpec.swift b/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridImageLoaderFactorySpec.swift index 550a70d1..0c74f5be 100644 --- a/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridImageLoaderFactorySpec.swift +++ b/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridImageLoaderFactorySpec.swift @@ -34,14 +34,14 @@ open class HybridImageLoaderFactorySpec_base { public init() { } public func getCxxWrapper() -> HybridImageLoaderFactorySpec_cxx { #if DEBUG - guard self is HybridImageLoaderFactorySpec else { + guard self is any HybridImageLoaderFactorySpec else { fatalError("`self` is not a `HybridImageLoaderFactorySpec`! Did you accidentally inherit from `HybridImageLoaderFactorySpec_base` instead of `HybridImageLoaderFactorySpec`?") } #endif if let cxxWrapper = self.cxxWrapper { return cxxWrapper } else { - let cxxWrapper = HybridImageLoaderFactorySpec_cxx(self as! HybridImageLoaderFactorySpec) + let cxxWrapper = HybridImageLoaderFactorySpec_cxx(self as! any HybridImageLoaderFactorySpec) self.cxxWrapper = cxxWrapper return cxxWrapper } diff --git a/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridImageLoaderSpec.swift b/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridImageLoaderSpec.swift index a0c5a4cc..29eb4503 100644 --- a/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridImageLoaderSpec.swift +++ b/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridImageLoaderSpec.swift @@ -32,14 +32,14 @@ open class HybridImageLoaderSpec_base { public init() { } public func getCxxWrapper() -> HybridImageLoaderSpec_cxx { #if DEBUG - guard self is HybridImageLoaderSpec else { + guard self is any HybridImageLoaderSpec else { fatalError("`self` is not a `HybridImageLoaderSpec`! Did you accidentally inherit from `HybridImageLoaderSpec_base` instead of `HybridImageLoaderSpec`?") } #endif if let cxxWrapper = self.cxxWrapper { return cxxWrapper } else { - let cxxWrapper = HybridImageLoaderSpec_cxx(self as! HybridImageLoaderSpec) + let cxxWrapper = HybridImageLoaderSpec_cxx(self as! any HybridImageLoaderSpec) self.cxxWrapper = cxxWrapper return cxxWrapper } diff --git a/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridImageSpec.swift b/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridImageSpec.swift index 67950054..6c836150 100644 --- a/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridImageSpec.swift +++ b/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridImageSpec.swift @@ -46,14 +46,14 @@ open class HybridImageSpec_base { public init() { } public func getCxxWrapper() -> HybridImageSpec_cxx { #if DEBUG - guard self is HybridImageSpec else { + guard self is any HybridImageSpec else { fatalError("`self` is not a `HybridImageSpec`! Did you accidentally inherit from `HybridImageSpec_base` instead of `HybridImageSpec`?") } #endif if let cxxWrapper = self.cxxWrapper { return cxxWrapper } else { - let cxxWrapper = HybridImageSpec_cxx(self as! HybridImageSpec) + let cxxWrapper = HybridImageSpec_cxx(self as! any HybridImageSpec) self.cxxWrapper = cxxWrapper return cxxWrapper } diff --git a/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridImageUtilsSpec.swift b/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridImageUtilsSpec.swift index fa81af45..8dba7932 100644 --- a/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridImageUtilsSpec.swift +++ b/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridImageUtilsSpec.swift @@ -32,14 +32,14 @@ open class HybridImageUtilsSpec_base { public init() { } public func getCxxWrapper() -> HybridImageUtilsSpec_cxx { #if DEBUG - guard self is HybridImageUtilsSpec else { + guard self is any HybridImageUtilsSpec else { fatalError("`self` is not a `HybridImageUtilsSpec`! Did you accidentally inherit from `HybridImageUtilsSpec_base` instead of `HybridImageUtilsSpec`?") } #endif if let cxxWrapper = self.cxxWrapper { return cxxWrapper } else { - let cxxWrapper = HybridImageUtilsSpec_cxx(self as! HybridImageUtilsSpec) + let cxxWrapper = HybridImageUtilsSpec_cxx(self as! any HybridImageUtilsSpec) self.cxxWrapper = cxxWrapper return cxxWrapper } diff --git a/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridNitroImageViewSpec.swift b/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridNitroImageViewSpec.swift index ae413da0..ba037731 100644 --- a/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridNitroImageViewSpec.swift +++ b/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridNitroImageViewSpec.swift @@ -32,14 +32,14 @@ open class HybridNitroImageViewSpec_base { public init() { } public func getCxxWrapper() -> HybridNitroImageViewSpec_cxx { #if DEBUG - guard self is HybridNitroImageViewSpec else { + guard self is any HybridNitroImageViewSpec else { fatalError("`self` is not a `HybridNitroImageViewSpec`! Did you accidentally inherit from `HybridNitroImageViewSpec_base` instead of `HybridNitroImageViewSpec`?") } #endif if let cxxWrapper = self.cxxWrapper { return cxxWrapper } else { - let cxxWrapper = HybridNitroImageViewSpec_cxx(self as! HybridNitroImageViewSpec) + let cxxWrapper = HybridNitroImageViewSpec_cxx(self as! any HybridNitroImageViewSpec) self.cxxWrapper = cxxWrapper return cxxWrapper } diff --git a/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridNitroImageViewSpec_cxx.swift b/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridNitroImageViewSpec_cxx.swift index 354d20de..85d519f2 100644 --- a/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridNitroImageViewSpec_cxx.swift +++ b/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridNitroImageViewSpec_cxx.swift @@ -232,4 +232,9 @@ open class HybridNitroImageViewSpec_cxx { public final func afterUpdate() { __implementation.afterUpdate() } + + public final func maybePrepareForRecycle() { + guard let recyclable = __implementation as? RecyclableView else { return } + recyclable.prepareForRecycle() + } } diff --git a/packages/react-native-nitro-image/package.json b/packages/react-native-nitro-image/package.json index 1b5b927f..8e5ffde4 100644 --- a/packages/react-native-nitro-image/package.json +++ b/packages/react-native-nitro-image/package.json @@ -62,10 +62,10 @@ "devDependencies": { "@biomejs/biome": "2.2.6", "@types/react": "^19.0.6", - "nitrogen": "0.32.1", + "nitrogen": "0.33.0", "react": "19.1.0", "react-native": "0.81.0", - "react-native-nitro-modules": "0.32.1", + "react-native-nitro-modules": "0.33.0", "typescript": "5.8.3" }, "peerDependencies": { diff --git a/packages/react-native-nitro-web-image/nitrogen/generated/ios/NitroWebImageAutolinking.mm b/packages/react-native-nitro-web-image/nitrogen/generated/ios/NitroWebImageAutolinking.mm index c5694ade..174f86e2 100644 --- a/packages/react-native-nitro-web-image/nitrogen/generated/ios/NitroWebImageAutolinking.mm +++ b/packages/react-native-nitro-web-image/nitrogen/generated/ios/NitroWebImageAutolinking.mm @@ -24,7 +24,7 @@ + (void) load { HybridObjectRegistry::registerHybridObjectConstructor( "WebImageFactory", []() -> std::shared_ptr { - std::shared_ptr hybridObject = NitroWebImage::NitroWebImageAutolinking::createWebImageFactory(); + std::shared_ptr hybridObject = NitroWebImage::NitroWebImageAutolinking::WebImageFactory::create(); return hybridObject; } ); diff --git a/packages/react-native-nitro-web-image/nitrogen/generated/ios/NitroWebImageAutolinking.swift b/packages/react-native-nitro-web-image/nitrogen/generated/ios/NitroWebImageAutolinking.swift index ef415b09..36a6631f 100644 --- a/packages/react-native-nitro-web-image/nitrogen/generated/ios/NitroWebImageAutolinking.swift +++ b/packages/react-native-nitro-web-image/nitrogen/generated/ios/NitroWebImageAutolinking.swift @@ -5,21 +5,40 @@ /// Copyright © Marc Rousavy @ Margelo /// +import NitroModules + +// TODO: Use empty enums once Swift supports exporting them as namespaces +// See: https://github.com/swiftlang/swift/pull/83616 public final class NitroWebImageAutolinking { public typealias bridge = margelo.nitro.web.image.bridge.swift - /** - * Creates an instance of a Swift class that implements `HybridWebImageFactorySpec`, - * and wraps it in a Swift class that can directly interop with C++ (`HybridWebImageFactorySpec_cxx`) - * - * This is generated by Nitrogen and will initialize the class specified - * in the `"autolinking"` property of `nitro.json` (in this case, `HybridWebImageFactory`). - */ - public static func createWebImageFactory() -> bridge.std__shared_ptr_HybridWebImageFactorySpec_ { - let hybridObject = HybridWebImageFactory() - return { () -> bridge.std__shared_ptr_HybridWebImageFactorySpec_ in - let __cxxWrapped = hybridObject.getCxxWrapper() - return __cxxWrapped.getCxxPart() - }() + private protocol AutolinkedClass { + associatedtype T + /** + * Creates an instance of the Swift class that implements the HybridObject's spec, + * and wraps it in a Swift class that can directly interop with C++. + * + * This is generated by Nitrogen and will initialize the class specified + * in the `"autolinking"` property of `nitro.json`. + */ + static func create() -> T + /** + * Returns whether this concrete implementation is also + * conforming to the `RecyclableView` protocol, or not. + */ + static var isRecyclableHybridView: Bool { get } + } + + public final class WebImageFactory: AutolinkedClass { + public static func create() -> bridge.std__shared_ptr_HybridWebImageFactorySpec_ { + let hybridObject = HybridWebImageFactory() + return { () -> bridge.std__shared_ptr_HybridWebImageFactorySpec_ in + let __cxxWrapped = hybridObject.getCxxWrapper() + return __cxxWrapped.getCxxPart() + }() + } + public static var isRecyclableHybridView: Bool { + return HybridWebImageFactory.self is any RecyclableView.Type + } } } diff --git a/packages/react-native-nitro-web-image/nitrogen/generated/ios/swift/HybridWebImageFactorySpec.swift b/packages/react-native-nitro-web-image/nitrogen/generated/ios/swift/HybridWebImageFactorySpec.swift index d8d03e76..716b30bc 100644 --- a/packages/react-native-nitro-web-image/nitrogen/generated/ios/swift/HybridWebImageFactorySpec.swift +++ b/packages/react-native-nitro-web-image/nitrogen/generated/ios/swift/HybridWebImageFactorySpec.swift @@ -33,14 +33,14 @@ open class HybridWebImageFactorySpec_base { public init() { } public func getCxxWrapper() -> HybridWebImageFactorySpec_cxx { #if DEBUG - guard self is HybridWebImageFactorySpec else { + guard self is any HybridWebImageFactorySpec else { fatalError("`self` is not a `HybridWebImageFactorySpec`! Did you accidentally inherit from `HybridWebImageFactorySpec_base` instead of `HybridWebImageFactorySpec`?") } #endif if let cxxWrapper = self.cxxWrapper { return cxxWrapper } else { - let cxxWrapper = HybridWebImageFactorySpec_cxx(self as! HybridWebImageFactorySpec) + let cxxWrapper = HybridWebImageFactorySpec_cxx(self as! any HybridWebImageFactorySpec) self.cxxWrapper = cxxWrapper return cxxWrapper } diff --git a/packages/react-native-nitro-web-image/package.json b/packages/react-native-nitro-web-image/package.json index fd2b7494..6be27462 100644 --- a/packages/react-native-nitro-web-image/package.json +++ b/packages/react-native-nitro-web-image/package.json @@ -61,10 +61,10 @@ "devDependencies": { "@biomejs/biome": "2.2.6", "@types/react": "^19.0.6", - "nitrogen": "0.32.1", + "nitrogen": "0.33.0", "react": "19.1.0", "react-native": "0.81.0", - "react-native-nitro-modules": "0.32.1", + "react-native-nitro-modules": "0.33.0", "react-native-nitro-image": "../react-native-nitro-image", "typescript": "5.8.3" },