diff --git a/ios/Podfile.lock b/ios/Podfile.lock index bccbd26..237f57f 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -8,6 +8,7 @@ PODS: - hermes-engine (0.14.0): - hermes-engine/Pre-built (= 0.14.0) - hermes-engine/Pre-built (0.14.0) + - lottie-ios (4.5.2) - NativeModules (0.0.0): - boost - DoubleConversion @@ -45,6 +46,7 @@ PODS: - fmt - glog - hermes-engine + - lottie-ios (= 4.5.2) - NitroModules - RCT-Folly - RCT-Folly/Fabric @@ -2775,6 +2777,7 @@ DEPENDENCIES: SPEC REPOS: trunk: + - lottie-ios - SocketRocket EXTERNAL SOURCES: @@ -2952,8 +2955,9 @@ SPEC CHECKSUMS: fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd glog: 5683914934d5b6e4240e497e0f4a3b42d1854183 hermes-engine: f7b1bbda11bd37179cfea60b5e240f9d508f457c + lottie-ios: 96784afc26ea031d3e2b6cae342a4b8915072489 NativeModules: ee8742cd9a988e76634d564f034090287b5e18a9 - NativeViews: ceee020f8f7ec32892b1225b50f0891486f1db83 + NativeViews: a8d4810f48eed13bb55807b364dcdd8c19e7eac4 NitroModules: 5bc319d441f4983894ea66b1d392c519536e6d23 RCT-Folly: 846fda9475e61ec7bcbf8a3fe81edfcaeb090669 RCTDeprecation: 2b70c6e3abe00396cefd8913efbf6a2db01a2b36 diff --git a/native-views/NativeViews.podspec b/native-views/NativeViews.podspec index f80a53c..41b00e9 100644 --- a/native-views/NativeViews.podspec +++ b/native-views/NativeViews.podspec @@ -21,6 +21,7 @@ Pod::Spec.new do |s| s.dependency 'React-jsi' s.dependency 'React-callinvoker' + s.dependency 'lottie-ios', '4.5.2' load 'nitrogen/generated/ios/NativeViews+autolinking.rb' add_nitrogen_files(s) diff --git a/native-views/android/build.gradle b/native-views/android/build.gradle index dd0fcca..3a2d727 100644 --- a/native-views/android/build.gradle +++ b/native-views/android/build.gradle @@ -124,5 +124,6 @@ def kotlin_version = getExtOrDefault("kotlinVersion") dependencies { implementation "com.facebook.react:react-android" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + implementation 'com.airbnb.android:lottie:6.7.1' implementation project(":react-native-nitro-modules") } diff --git a/native-views/android/src/main/java/com/margelo/nitro/nativeviews/HybridLottie.kt b/native-views/android/src/main/java/com/margelo/nitro/nativeviews/HybridLottie.kt new file mode 100644 index 0000000..0a4abc6 --- /dev/null +++ b/native-views/android/src/main/java/com/margelo/nitro/nativeviews/HybridLottie.kt @@ -0,0 +1,20 @@ +package com.margelo.nitro.nativeviews + +import com.airbnb.lottie.LottieAnimationView +import com.airbnb.lottie.LottieDrawable +import com.facebook.react.uimanager.ThemedReactContext + +class HybridLottie(val context: ThemedReactContext): HybridLottieSpec() { + override val view: LottieAnimationView = LottieAnimationView(context).apply { + repeatCount = LottieDrawable.INFINITE + } + + private var _url: String = "" + override var url: String + get() = _url + set(value) { + _url = value + view.setAnimationFromUrl(value) + view.playAnimation() + } +} \ No newline at end of file diff --git a/native-views/android/src/main/java/com/margelo/nitro/nativeviews/NativeViewsPackage.kt b/native-views/android/src/main/java/com/margelo/nitro/nativeviews/NativeViewsPackage.kt index 87fc106..bdecfef 100644 --- a/native-views/android/src/main/java/com/margelo/nitro/nativeviews/NativeViewsPackage.kt +++ b/native-views/android/src/main/java/com/margelo/nitro/nativeviews/NativeViewsPackage.kt @@ -5,6 +5,7 @@ import com.facebook.react.bridge.NativeModule import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.module.model.ReactModuleInfoProvider import com.facebook.react.uimanager.ViewManager +import com.margelo.nitro.nativeviews.views.HybridLottieManager import com.margelo.nitro.nativeviews.views.HybridNativeViewsManager @@ -18,7 +19,7 @@ class NativeViewsPackage : BaseReactPackage() { } override fun createViewManagers(reactContext: ReactApplicationContext): List> { - return listOf(HybridNativeViewsManager()) + return listOf(HybridNativeViewsManager(), HybridLottieManager()) } companion object { diff --git a/native-views/ios/LottieView.swift b/native-views/ios/LottieView.swift new file mode 100644 index 0000000..802fbe1 --- /dev/null +++ b/native-views/ios/LottieView.swift @@ -0,0 +1,28 @@ +// +// LottieView.swift +// Pods +// +// Created by Parsa Nasirimehr on 2025-12-14. +// + +import Foundation +import Lottie + +class HybridLottie : HybridLottieSpec { + var view: UIView = LottieAnimationView() + + var url: String = "" { + didSet { + guard let url = URL(string: url) else { return } + Task { + guard let animation = await LottieAnimation.loadedFrom(url: url) else { return } + await MainActor.run { + guard let lottieView = view as? LottieAnimationView else { return } + lottieView.animation = animation + lottieView.loopMode = .loop + lottieView.play() + } + } + } + } +} diff --git a/native-views/nitro.json b/native-views/nitro.json index f8ca39d..7ef11f4 100644 --- a/native-views/nitro.json +++ b/native-views/nitro.json @@ -11,6 +11,10 @@ "NativeViews": { "swift": "HybridNativeViews", "kotlin": "HybridNativeViews" + }, + "Lottie": { + "swift": "HybridLottie", + "kotlin": "HybridLottie" } }, "ignorePaths": ["node_modules"] diff --git a/native-views/nitrogen/generated/android/c++/JHybridLottieSpec.cpp b/native-views/nitrogen/generated/android/c++/JHybridLottieSpec.cpp new file mode 100644 index 0000000..1cd1ae4 --- /dev/null +++ b/native-views/nitrogen/generated/android/c++/JHybridLottieSpec.cpp @@ -0,0 +1,56 @@ +/// +/// JHybridLottieSpec.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2025 Marc Rousavy @ Margelo +/// + +#include "JHybridLottieSpec.hpp" + + + +#include + +namespace margelo::nitro::nativeviews { + + jni::local_ref JHybridLottieSpec::initHybrid(jni::alias_ref jThis) { + return makeCxxInstance(jThis); + } + + void JHybridLottieSpec::registerNatives() { + registerHybrid({ + makeNativeMethod("initHybrid", JHybridLottieSpec::initHybrid), + }); + } + + size_t JHybridLottieSpec::getExternalMemorySize() noexcept { + static const auto method = javaClassStatic()->getMethod("getMemorySize"); + return method(_javaPart); + } + + void JHybridLottieSpec::dispose() noexcept { + static const auto method = javaClassStatic()->getMethod("dispose"); + method(_javaPart); + } + + std::string JHybridLottieSpec::toString() { + static const auto method = javaClassStatic()->getMethod("toString"); + auto javaString = method(_javaPart); + return javaString->toStdString(); + } + + // Properties + std::string JHybridLottieSpec::getUrl() { + static const auto method = javaClassStatic()->getMethod()>("getUrl"); + auto __result = method(_javaPart); + return __result->toStdString(); + } + void JHybridLottieSpec::setUrl(const std::string& url) { + static const auto method = javaClassStatic()->getMethod /* url */)>("setUrl"); + method(_javaPart, jni::make_jstring(url)); + } + + // Methods + + +} // namespace margelo::nitro::nativeviews diff --git a/native-views/nitrogen/generated/android/c++/JHybridLottieSpec.hpp b/native-views/nitrogen/generated/android/c++/JHybridLottieSpec.hpp new file mode 100644 index 0000000..00ce158 --- /dev/null +++ b/native-views/nitrogen/generated/android/c++/JHybridLottieSpec.hpp @@ -0,0 +1,66 @@ +/// +/// HybridLottieSpec.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2025 Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include +#include "HybridLottieSpec.hpp" + + + + +namespace margelo::nitro::nativeviews { + + using namespace facebook; + + class JHybridLottieSpec: public jni::HybridClass, + public virtual HybridLottieSpec { + public: + static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/nativeviews/HybridLottieSpec;"; + static jni::local_ref initHybrid(jni::alias_ref jThis); + static void registerNatives(); + + protected: + // C++ constructor (called from Java via `initHybrid()`) + explicit JHybridLottieSpec(jni::alias_ref jThis) : + HybridObject(HybridLottieSpec::TAG), + HybridBase(jThis), + _javaPart(jni::make_global(jThis)) {} + + public: + ~JHybridLottieSpec() override { + // Hermes GC can destroy JS objects on a non-JNI Thread. + jni::ThreadScope::WithClassLoader([&] { _javaPart.reset(); }); + } + + public: + size_t getExternalMemorySize() noexcept override; + void dispose() noexcept override; + std::string toString() override; + + public: + inline const jni::global_ref& getJavaPart() const noexcept { + return _javaPart; + } + + public: + // Properties + std::string getUrl() override; + void setUrl(const std::string& url) override; + + public: + // Methods + + + private: + friend HybridBase; + using HybridBase::HybridBase; + jni::global_ref _javaPart; + }; + +} // namespace margelo::nitro::nativeviews diff --git a/native-views/nitrogen/generated/android/c++/views/JHybridLottieStateUpdater.cpp b/native-views/nitrogen/generated/android/c++/views/JHybridLottieStateUpdater.cpp new file mode 100644 index 0000000..7b744e1 --- /dev/null +++ b/native-views/nitrogen/generated/android/c++/views/JHybridLottieStateUpdater.cpp @@ -0,0 +1,56 @@ +/// +/// JHybridLottieStateUpdater.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2025 Marc Rousavy @ Margelo +/// + +#include "JHybridLottieStateUpdater.hpp" +#include "views/HybridLottieComponent.hpp" +#include + +namespace margelo::nitro::nativeviews::views { + +using namespace facebook; +using ConcreteStateData = react::ConcreteState; + +void JHybridLottieStateUpdater::updateViewProps(jni::alias_ref /* class */, + jni::alias_ref javaView, + jni::alias_ref stateWrapperInterface) { + JHybridLottieSpec* view = javaView->cthis(); + + // Get concrete StateWrapperImpl from passed StateWrapper interface object + jobject rawStateWrapper = stateWrapperInterface.get(); + if (!stateWrapperInterface->isInstanceOf(react::StateWrapperImpl::javaClassStatic())) { + throw std::runtime_error("StateWrapper is not a StateWrapperImpl"); + } + auto stateWrapper = jni::alias_ref{ + static_cast(rawStateWrapper)}; + + std::shared_ptr state = stateWrapper->cthis()->getState(); + auto concreteState = std::dynamic_pointer_cast(state); + const HybridLottieState& data = concreteState->getData(); + const std::optional& maybeProps = data.getProps(); + if (!maybeProps.has_value()) { + // Props aren't set yet! + throw std::runtime_error("HybridLottieState's data doesn't contain any props!"); + } + const HybridLottieProps& props = maybeProps.value(); + if (props.url.isDirty) { + view->setUrl(props.url.value); + // TODO: Set isDirty = false + } + + // Update hybridRef if it changed + if (props.hybridRef.isDirty) { + // hybridRef changed - call it with new this + const auto& maybeFunc = props.hybridRef.value; + if (maybeFunc.has_value()) { + std::shared_ptr shared = javaView->cthis()->shared_cast(); + maybeFunc.value()(shared); + } + // TODO: Set isDirty = false + } +} + +} // namespace margelo::nitro::nativeviews::views diff --git a/native-views/nitrogen/generated/android/c++/views/JHybridLottieStateUpdater.hpp b/native-views/nitrogen/generated/android/c++/views/JHybridLottieStateUpdater.hpp new file mode 100644 index 0000000..cfb1283 --- /dev/null +++ b/native-views/nitrogen/generated/android/c++/views/JHybridLottieStateUpdater.hpp @@ -0,0 +1,49 @@ +/// +/// JHybridLottieStateUpdater.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2025 Marc Rousavy @ Margelo +/// + +#pragma once + +#ifndef RN_SERIALIZABLE_STATE +#error nativeviews was compiled without the 'RN_SERIALIZABLE_STATE' flag. This flag is required for Nitro Views - set it in your CMakeLists! +#endif + +#include +#include +#include +#include +#include +#include +#include "JHybridLottieSpec.hpp" +#include "views/HybridLottieComponent.hpp" + +namespace margelo::nitro::nativeviews::views { + +using namespace facebook; + +class JHybridLottieStateUpdater: public jni::JavaClass { +public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/nativeviews/views/HybridLottieStateUpdater;"; + +public: + static void updateViewProps(jni::alias_ref /* class */, + jni::alias_ref view, + jni::alias_ref stateWrapperInterface); + +public: + static void registerNatives() { + // Register JNI calls + javaClassStatic()->registerNatives({ + makeNativeMethod("updateViewProps", JHybridLottieStateUpdater::updateViewProps), + }); + // Register React Native view component descriptor + auto provider = react::concreteComponentDescriptorProvider(); + auto providerRegistry = react::CoreComponentsRegistry::sharedProviderRegistry(); + providerRegistry->add(provider); + } +}; + +} // namespace margelo::nitro::nativeviews::views diff --git a/native-views/nitrogen/generated/android/kotlin/com/margelo/nitro/nativeviews/HybridLottieSpec.kt b/native-views/nitrogen/generated/android/kotlin/com/margelo/nitro/nativeviews/HybridLottieSpec.kt new file mode 100644 index 0000000..58b9bc9 --- /dev/null +++ b/native-views/nitrogen/generated/android/kotlin/com/margelo/nitro/nativeviews/HybridLottieSpec.kt @@ -0,0 +1,59 @@ +/// +/// HybridLottieSpec.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2025 Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.nativeviews + +import androidx.annotation.Keep +import com.facebook.jni.HybridData +import com.facebook.proguard.annotations.DoNotStrip +import com.margelo.nitro.views.HybridView + +/** + * A Kotlin class representing the Lottie HybridObject. + * Implement this abstract class to create Kotlin-based instances of Lottie. + */ +@DoNotStrip +@Keep +@Suppress( + "KotlinJniMissingFunction", "unused", + "RedundantSuppression", "RedundantUnitReturnType", "SimpleRedundantLet", + "LocalVariableName", "PropertyName", "PrivatePropertyName", "FunctionName" +) +abstract class HybridLottieSpec: HybridView() { + @DoNotStrip + private var mHybridData: HybridData = initHybrid() + + init { + super.updateNative(mHybridData) + } + + override fun updateNative(hybridData: HybridData) { + mHybridData = hybridData + super.updateNative(hybridData) + } + + // Default implementation of `HybridObject.toString()` + override fun toString(): String { + return "[HybridObject Lottie]" + } + + // Properties + @get:DoNotStrip + @get:Keep + @set:DoNotStrip + @set:Keep + abstract var url: String + + // Methods + + + private external fun initHybrid(): HybridData + + companion object { + protected const val TAG = "HybridLottieSpec" + } +} diff --git a/native-views/nitrogen/generated/android/kotlin/com/margelo/nitro/nativeviews/views/HybridLottieManager.kt b/native-views/nitrogen/generated/android/kotlin/com/margelo/nitro/nativeviews/views/HybridLottieManager.kt new file mode 100644 index 0000000..12414c4 --- /dev/null +++ b/native-views/nitrogen/generated/android/kotlin/com/margelo/nitro/nativeviews/views/HybridLottieManager.kt @@ -0,0 +1,50 @@ +/// +/// HybridLottieManager.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2025 Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.nativeviews.views + +import android.view.View +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.nativeviews.* + +/** + * Represents the React Native `ViewManager` for the "Lottie" Nitro HybridView. + */ +open class HybridLottieManager: SimpleViewManager() { + private val views = hashMapOf() + + override fun getName(): String { + return "Lottie" + } + + override fun createViewInstance(reactContext: ThemedReactContext): View { + val hybridView = HybridLottie(reactContext) + val view = hybridView.view + views[view] = 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!") + + // 1. Update each prop individually + hybridView.beforeUpdate() + HybridLottieStateUpdater.updateViewProps(hybridView, stateWrapper) + hybridView.afterUpdate() + + // 2. Continue in base View props + return super.updateState(view, props, stateWrapper) + } +} diff --git a/native-views/nitrogen/generated/android/kotlin/com/margelo/nitro/nativeviews/views/HybridLottieStateUpdater.kt b/native-views/nitrogen/generated/android/kotlin/com/margelo/nitro/nativeviews/views/HybridLottieStateUpdater.kt new file mode 100644 index 0000000..0b347d8 --- /dev/null +++ b/native-views/nitrogen/generated/android/kotlin/com/margelo/nitro/nativeviews/views/HybridLottieStateUpdater.kt @@ -0,0 +1,23 @@ +/// +/// HybridLottieStateUpdater.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2025 Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.nativeviews.views + +import com.facebook.react.uimanager.StateWrapper +import com.margelo.nitro.nativeviews.* + +internal class HybridLottieStateUpdater { + companion object { + /** + * Updates the props for [view] through C++. + * The [state] prop is expected to contain [view]'s props as wrapped Fabric state. + */ + @Suppress("KotlinJniMissingFunction") + @JvmStatic + external fun updateViewProps(view: HybridLottieSpec, state: StateWrapper) + } +} diff --git a/native-views/nitrogen/generated/android/nativeviews+autolinking.cmake b/native-views/nitrogen/generated/android/nativeviews+autolinking.cmake index e1a5e5d..3f93618 100644 --- a/native-views/nitrogen/generated/android/nativeviews+autolinking.cmake +++ b/native-views/nitrogen/generated/android/nativeviews+autolinking.cmake @@ -33,9 +33,13 @@ target_sources( # Autolinking Setup ../nitrogen/generated/android/nativeviewsOnLoad.cpp # Shared Nitrogen C++ sources + ../nitrogen/generated/shared/c++/HybridLottieSpec.cpp + ../nitrogen/generated/shared/c++/views/HybridLottieComponent.cpp ../nitrogen/generated/shared/c++/HybridNativeViewsSpec.cpp ../nitrogen/generated/shared/c++/views/HybridNativeViewsComponent.cpp # Android-specific Nitrogen C++ sources + ../nitrogen/generated/android/c++/JHybridLottieSpec.cpp + ../nitrogen/generated/android/c++/views/JHybridLottieStateUpdater.cpp ../nitrogen/generated/android/c++/JHybridNativeViewsSpec.cpp ../nitrogen/generated/android/c++/views/JHybridNativeViewsStateUpdater.cpp ) diff --git a/native-views/nitrogen/generated/android/nativeviewsOnLoad.cpp b/native-views/nitrogen/generated/android/nativeviewsOnLoad.cpp index 3ab5b5c..2248237 100644 --- a/native-views/nitrogen/generated/android/nativeviewsOnLoad.cpp +++ b/native-views/nitrogen/generated/android/nativeviewsOnLoad.cpp @@ -15,6 +15,8 @@ #include #include +#include "JHybridLottieSpec.hpp" +#include "views/JHybridLottieStateUpdater.hpp" #include "JHybridNativeViewsSpec.hpp" #include "views/JHybridNativeViewsStateUpdater.hpp" #include @@ -28,6 +30,8 @@ int initialize(JavaVM* vm) { return facebook::jni::initialize(vm, [] { // Register native JNI methods + margelo::nitro::nativeviews::JHybridLottieSpec::registerNatives(); + margelo::nitro::nativeviews::views::JHybridLottieStateUpdater::registerNatives(); margelo::nitro::nativeviews::JHybridNativeViewsSpec::registerNatives(); margelo::nitro::nativeviews::views::JHybridNativeViewsStateUpdater::registerNatives(); @@ -40,6 +44,14 @@ int initialize(JavaVM* vm) { return instance->cthis()->shared(); } ); + HybridObjectRegistry::registerHybridObjectConstructor( + "Lottie", + []() -> std::shared_ptr { + static DefaultConstructableObject object("com/margelo/nitro/nativeviews/HybridLottie"); + auto instance = object.create(); + return instance->cthis()->shared(); + } + ); }); } diff --git a/native-views/nitrogen/generated/ios/NativeViews-Swift-Cxx-Bridge.cpp b/native-views/nitrogen/generated/ios/NativeViews-Swift-Cxx-Bridge.cpp index 036fa41..262329e 100644 --- a/native-views/nitrogen/generated/ios/NativeViews-Swift-Cxx-Bridge.cpp +++ b/native-views/nitrogen/generated/ios/NativeViews-Swift-Cxx-Bridge.cpp @@ -8,12 +8,29 @@ #include "NativeViews-Swift-Cxx-Bridge.hpp" // Include C++ implementation defined types +#include "HybridLottieSpecSwift.hpp" #include "HybridNativeViewsSpecSwift.hpp" #include "NativeViews-Swift-Cxx-Umbrella.hpp" #include namespace margelo::nitro::nativeviews::bridge::swift { + // pragma MARK: std::shared_ptr + std::shared_ptr create_std__shared_ptr_HybridLottieSpec_(void* NON_NULL swiftUnsafePointer) noexcept { + NativeViews::HybridLottieSpec_cxx swiftPart = NativeViews::HybridLottieSpec_cxx::fromUnsafe(swiftUnsafePointer); + return std::make_shared(swiftPart); + } + void* NON_NULL get_std__shared_ptr_HybridLottieSpec_(std__shared_ptr_HybridLottieSpec_ cppType) { + std::shared_ptr swiftWrapper = std::dynamic_pointer_cast(cppType); + #ifdef NITRO_DEBUG + if (swiftWrapper == nullptr) [[unlikely]] { + throw std::runtime_error("Class \"HybridLottieSpec\" is not implemented in Swift!"); + } + #endif + NativeViews::HybridLottieSpec_cxx& swiftPart = swiftWrapper->getSwiftPart(); + return swiftPart.toUnsafe(); + } + // pragma MARK: std::shared_ptr std::shared_ptr create_std__shared_ptr_HybridNativeViewsSpec_(void* NON_NULL swiftUnsafePointer) noexcept { NativeViews::HybridNativeViewsSpec_cxx swiftPart = NativeViews::HybridNativeViewsSpec_cxx::fromUnsafe(swiftUnsafePointer); diff --git a/native-views/nitrogen/generated/ios/NativeViews-Swift-Cxx-Bridge.hpp b/native-views/nitrogen/generated/ios/NativeViews-Swift-Cxx-Bridge.hpp index 2fe4315..f8d05ca 100644 --- a/native-views/nitrogen/generated/ios/NativeViews-Swift-Cxx-Bridge.hpp +++ b/native-views/nitrogen/generated/ios/NativeViews-Swift-Cxx-Bridge.hpp @@ -8,14 +8,19 @@ #pragma once // Forward declarations of C++ defined types +// Forward declaration of `HybridLottieSpec` to properly resolve imports. +namespace margelo::nitro::nativeviews { class HybridLottieSpec; } // Forward declaration of `HybridNativeViewsSpec` to properly resolve imports. namespace margelo::nitro::nativeviews { class HybridNativeViewsSpec; } // Forward declarations of Swift defined types +// Forward declaration of `HybridLottieSpec_cxx` to properly resolve imports. +namespace NativeViews { class HybridLottieSpec_cxx; } // Forward declaration of `HybridNativeViewsSpec_cxx` to properly resolve imports. namespace NativeViews { class HybridNativeViewsSpec_cxx; } // Include C++ defined types +#include "HybridLottieSpec.hpp" #include "HybridNativeViewsSpec.hpp" #include @@ -25,6 +30,18 @@ namespace NativeViews { class HybridNativeViewsSpec_cxx; } */ namespace margelo::nitro::nativeviews::bridge::swift { + // pragma MARK: std::shared_ptr + /** + * Specialized version of `std::shared_ptr`. + */ + using std__shared_ptr_HybridLottieSpec_ = std::shared_ptr; + std::shared_ptr create_std__shared_ptr_HybridLottieSpec_(void* NON_NULL swiftUnsafePointer) noexcept; + void* NON_NULL get_std__shared_ptr_HybridLottieSpec_(std__shared_ptr_HybridLottieSpec_ cppType); + + // pragma MARK: std::weak_ptr + using std__weak_ptr_HybridLottieSpec_ = std::weak_ptr; + inline std__weak_ptr_HybridLottieSpec_ weakify_std__shared_ptr_HybridLottieSpec_(const std::shared_ptr& strong) noexcept { return strong; } + // pragma MARK: std::shared_ptr /** * Specialized version of `std::shared_ptr`. diff --git a/native-views/nitrogen/generated/ios/NativeViews-Swift-Cxx-Umbrella.hpp b/native-views/nitrogen/generated/ios/NativeViews-Swift-Cxx-Umbrella.hpp index a7a2102..f04eb19 100644 --- a/native-views/nitrogen/generated/ios/NativeViews-Swift-Cxx-Umbrella.hpp +++ b/native-views/nitrogen/generated/ios/NativeViews-Swift-Cxx-Umbrella.hpp @@ -8,10 +8,13 @@ #pragma once // Forward declarations of C++ defined types +// Forward declaration of `HybridLottieSpec` to properly resolve imports. +namespace margelo::nitro::nativeviews { class HybridLottieSpec; } // Forward declaration of `HybridNativeViewsSpec` to properly resolve imports. namespace margelo::nitro::nativeviews { class HybridNativeViewsSpec; } // Include C++ defined types +#include "HybridLottieSpec.hpp" #include "HybridNativeViewsSpec.hpp" #include #include @@ -26,6 +29,8 @@ namespace margelo::nitro::nativeviews { class HybridNativeViewsSpec; } #include // Forward declarations of Swift defined types +// Forward declaration of `HybridLottieSpec_cxx` to properly resolve imports. +namespace NativeViews { class HybridLottieSpec_cxx; } // Forward declaration of `HybridNativeViewsSpec_cxx` to properly resolve imports. namespace NativeViews { class HybridNativeViewsSpec_cxx; } diff --git a/native-views/nitrogen/generated/ios/NativeViewsAutolinking.mm b/native-views/nitrogen/generated/ios/NativeViewsAutolinking.mm index 5db2a30..462397e 100644 --- a/native-views/nitrogen/generated/ios/NativeViewsAutolinking.mm +++ b/native-views/nitrogen/generated/ios/NativeViewsAutolinking.mm @@ -11,6 +11,7 @@ #import #include "HybridNativeViewsSpecSwift.hpp" +#include "HybridLottieSpecSwift.hpp" @interface NativeViewsAutolinking : NSObject @end @@ -28,6 +29,13 @@ + (void) load { return hybridObject; } ); + HybridObjectRegistry::registerHybridObjectConstructor( + "Lottie", + []() -> std::shared_ptr { + std::shared_ptr hybridObject = NativeViews::NativeViewsAutolinking::createLottie(); + return hybridObject; + } + ); } @end diff --git a/native-views/nitrogen/generated/ios/NativeViewsAutolinking.swift b/native-views/nitrogen/generated/ios/NativeViewsAutolinking.swift index 5e119a2..82b2bc7 100644 --- a/native-views/nitrogen/generated/ios/NativeViewsAutolinking.swift +++ b/native-views/nitrogen/generated/ios/NativeViewsAutolinking.swift @@ -22,4 +22,19 @@ public final class NativeViewsAutolinking { return __cxxWrapped.getCxxPart() }() } + + /** + * Creates an instance of a Swift class that implements `HybridLottieSpec`, + * and wraps it in a Swift class that can directly interop with C++ (`HybridLottieSpec_cxx`) + * + * This is generated by Nitrogen and will initialize the class specified + * in the `"autolinking"` property of `nitro.json` (in this case, `HybridLottie`). + */ + public static func createLottie() -> bridge.std__shared_ptr_HybridLottieSpec_ { + let hybridObject = HybridLottie() + return { () -> bridge.std__shared_ptr_HybridLottieSpec_ in + let __cxxWrapped = hybridObject.getCxxWrapper() + return __cxxWrapped.getCxxPart() + }() + } } diff --git a/native-views/nitrogen/generated/ios/c++/HybridLottieSpecSwift.cpp b/native-views/nitrogen/generated/ios/c++/HybridLottieSpecSwift.cpp new file mode 100644 index 0000000..458269a --- /dev/null +++ b/native-views/nitrogen/generated/ios/c++/HybridLottieSpecSwift.cpp @@ -0,0 +1,11 @@ +/// +/// HybridLottieSpecSwift.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2025 Marc Rousavy @ Margelo +/// + +#include "HybridLottieSpecSwift.hpp" + +namespace margelo::nitro::nativeviews { +} // namespace margelo::nitro::nativeviews diff --git a/native-views/nitrogen/generated/ios/c++/HybridLottieSpecSwift.hpp b/native-views/nitrogen/generated/ios/c++/HybridLottieSpecSwift.hpp new file mode 100644 index 0000000..bd75c49 --- /dev/null +++ b/native-views/nitrogen/generated/ios/c++/HybridLottieSpecSwift.hpp @@ -0,0 +1,75 @@ +/// +/// HybridLottieSpecSwift.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2025 Marc Rousavy @ Margelo +/// + +#pragma once + +#include "HybridLottieSpec.hpp" + +// Forward declaration of `HybridLottieSpec_cxx` to properly resolve imports. +namespace NativeViews { class HybridLottieSpec_cxx; } + + + +#include + +#include "NativeViews-Swift-Cxx-Umbrella.hpp" + +namespace margelo::nitro::nativeviews { + + /** + * The C++ part of HybridLottieSpec_cxx.swift. + * + * HybridLottieSpecSwift (C++) accesses HybridLottieSpec_cxx (Swift), and might + * contain some additional bridging code for C++ <> Swift interop. + * + * Since this obviously introduces an overhead, I hope at some point in + * the future, HybridLottieSpec_cxx can directly inherit from the C++ class HybridLottieSpec + * to simplify the whole structure and memory management. + */ + class HybridLottieSpecSwift: public virtual HybridLottieSpec { + public: + // Constructor from a Swift instance + explicit HybridLottieSpecSwift(const NativeViews::HybridLottieSpec_cxx& swiftPart): + HybridObject(HybridLottieSpec::TAG), + _swiftPart(swiftPart) { } + + public: + // Get the Swift part + inline NativeViews::HybridLottieSpec_cxx& getSwiftPart() noexcept { + return _swiftPart; + } + + public: + inline size_t getExternalMemorySize() noexcept override { + return _swiftPart.getMemorySize(); + } + void dispose() noexcept override { + _swiftPart.dispose(); + } + std::string toString() override { + return _swiftPart.toString(); + } + + public: + // Properties + inline std::string getUrl() noexcept override { + auto __result = _swiftPart.getUrl(); + return __result; + } + inline void setUrl(const std::string& url) noexcept override { + _swiftPart.setUrl(url); + } + + public: + // Methods + + + private: + NativeViews::HybridLottieSpec_cxx _swiftPart; + }; + +} // namespace margelo::nitro::nativeviews diff --git a/native-views/nitrogen/generated/ios/c++/views/HybridLottieComponent.mm b/native-views/nitrogen/generated/ios/c++/views/HybridLottieComponent.mm new file mode 100644 index 0000000..2907071 --- /dev/null +++ b/native-views/nitrogen/generated/ios/c++/views/HybridLottieComponent.mm @@ -0,0 +1,96 @@ +/// +/// HybridLottieComponent.mm +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2025 Marc Rousavy @ Margelo +/// + +#import "HybridLottieComponent.hpp" +#import +#import +#import +#import +#import +#import +#import + +#import "HybridLottieSpecSwift.hpp" +#import "NativeViews-Swift-Cxx-Umbrella.hpp" + +using namespace facebook; +using namespace margelo::nitro::nativeviews; +using namespace margelo::nitro::nativeviews::views; + +/** + * Represents the React Native View holder for the Nitro "Lottie" HybridView. + */ +@interface HybridLottieComponent: RCTViewComponentView +@end + +@implementation HybridLottieComponent { + std::shared_ptr _hybridView; +} + ++ (void) load { + [super load]; + [RCTComponentViewFactory.currentComponentViewFactory registerComponentViewClass:[HybridLottieComponent class]]; +} + ++ (react::ComponentDescriptorProvider) componentDescriptorProvider { + return react::concreteComponentDescriptorProvider(); +} + +- (instancetype) init { + if (self = [super init]) { + std::shared_ptr hybridView = NativeViews::NativeViewsAutolinking::createLottie(); + _hybridView = std::dynamic_pointer_cast(hybridView); + [self updateView]; + } + return self; +} + +- (void) updateView { + // 1. Get Swift part + NativeViews::HybridLottieSpec_cxx& swiftPart = _hybridView->getSwiftPart(); + + // 2. Get UIView* + void* viewUnsafe = swiftPart.getView(); + UIView* view = (__bridge_transfer UIView*) viewUnsafe; + + // 3. Update RCTViewComponentView's [contentView] + [self setContentView:view]; +} + +- (void) updateProps:(const std::shared_ptr&)props + oldProps:(const std::shared_ptr&)oldProps { + // 1. Downcast props + const auto& newViewPropsConst = *std::static_pointer_cast(props); + auto& newViewProps = const_cast(newViewPropsConst); + NativeViews::HybridLottieSpec_cxx& swiftPart = _hybridView->getSwiftPart(); + + // 2. Update each prop individually + swiftPart.beforeUpdate(); + + // url: string + if (newViewProps.url.isDirty) { + swiftPart.setUrl(newViewProps.url.value); + newViewProps.url.isDirty = false; + } + + swiftPart.afterUpdate(); + + // 3. Update hybridRef if it changed + if (newViewProps.hybridRef.isDirty) { + // hybridRef changed - call it with new this + const auto& maybeFunc = newViewProps.hybridRef.value; + if (maybeFunc.has_value()) { + maybeFunc.value()(_hybridView); + } + newViewProps.hybridRef.isDirty = false; + } + + // 4. Continue in base class + [super updateProps:props oldProps:oldProps]; +} + +@end diff --git a/native-views/nitrogen/generated/ios/swift/HybridLottieSpec.swift b/native-views/nitrogen/generated/ios/swift/HybridLottieSpec.swift new file mode 100644 index 0000000..79255e8 --- /dev/null +++ b/native-views/nitrogen/generated/ios/swift/HybridLottieSpec.swift @@ -0,0 +1,56 @@ +/// +/// HybridLottieSpec.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2025 Marc Rousavy @ Margelo +/// + +import Foundation +import NitroModules + +/// See ``HybridLottieSpec`` +public protocol HybridLottieSpec_protocol: HybridObject, HybridView { + // Properties + var url: String { get set } + + // Methods + +} + +public extension HybridLottieSpec_protocol { + /// Default implementation of ``HybridObject.toString`` + func toString() -> String { + return "[HybridObject Lottie]" + } +} + +/// See ``HybridLottieSpec`` +open class HybridLottieSpec_base { + private weak var cxxWrapper: HybridLottieSpec_cxx? = nil + public init() { } + public func getCxxWrapper() -> HybridLottieSpec_cxx { + #if DEBUG + guard self is HybridLottieSpec else { + fatalError("`self` is not a `HybridLottieSpec`! Did you accidentally inherit from `HybridLottieSpec_base` instead of `HybridLottieSpec`?") + } + #endif + if let cxxWrapper = self.cxxWrapper { + return cxxWrapper + } else { + let cxxWrapper = HybridLottieSpec_cxx(self as! HybridLottieSpec) + self.cxxWrapper = cxxWrapper + return cxxWrapper + } + } +} + +/** + * A Swift base-protocol representing the Lottie HybridObject. + * Implement this protocol to create Swift-based instances of Lottie. + * ```swift + * class HybridLottie : HybridLottieSpec { + * // ... + * } + * ``` + */ +public typealias HybridLottieSpec = HybridLottieSpec_protocol & HybridLottieSpec_base diff --git a/native-views/nitrogen/generated/ios/swift/HybridLottieSpec_cxx.swift b/native-views/nitrogen/generated/ios/swift/HybridLottieSpec_cxx.swift new file mode 100644 index 0000000..9cd0652 --- /dev/null +++ b/native-views/nitrogen/generated/ios/swift/HybridLottieSpec_cxx.swift @@ -0,0 +1,140 @@ +/// +/// HybridLottieSpec_cxx.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2025 Marc Rousavy @ Margelo +/// + +import Foundation +import NitroModules + +/** + * A class implementation that bridges HybridLottieSpec over to C++. + * In C++, we cannot use Swift protocols - so we need to wrap it in a class to make it strongly defined. + * + * Also, some Swift types need to be bridged with special handling: + * - Enums need to be wrapped in Structs, otherwise they cannot be accessed bi-directionally (Swift bug: https://github.com/swiftlang/swift/issues/75330) + * - Other HybridObjects need to be wrapped/unwrapped from the Swift TCxx wrapper + * - Throwing methods need to be wrapped with a Result type, as exceptions cannot be propagated to C++ + */ +open class HybridLottieSpec_cxx { + /** + * The Swift <> C++ bridge's namespace (`margelo::nitro::nativeviews::bridge::swift`) + * from `NativeViews-Swift-Cxx-Bridge.hpp`. + * This contains specialized C++ templates, and C++ helper functions that can be accessed from Swift. + */ + public typealias bridge = margelo.nitro.nativeviews.bridge.swift + + /** + * Holds an instance of the `HybridLottieSpec` Swift protocol. + */ + private var __implementation: any HybridLottieSpec + + /** + * Holds a weak pointer to the C++ class that wraps the Swift class. + */ + private var __cxxPart: bridge.std__weak_ptr_HybridLottieSpec_ + + /** + * Create a new `HybridLottieSpec_cxx` that wraps the given `HybridLottieSpec`. + * All properties and methods bridge to C++ types. + */ + public init(_ implementation: any HybridLottieSpec) { + self.__implementation = implementation + self.__cxxPart = .init() + /* no base class */ + } + + /** + * Get the actual `HybridLottieSpec` instance this class wraps. + */ + @inline(__always) + public func getHybridLottieSpec() -> any HybridLottieSpec { + return __implementation + } + + /** + * Casts this instance to a retained unsafe raw pointer. + * This acquires one additional strong reference on the object! + */ + public func toUnsafe() -> UnsafeMutableRawPointer { + return Unmanaged.passRetained(self).toOpaque() + } + + /** + * Casts an unsafe pointer to a `HybridLottieSpec_cxx`. + * The pointer has to be a retained opaque `Unmanaged`. + * This removes one strong reference from the object! + */ + public class func fromUnsafe(_ pointer: UnsafeMutableRawPointer) -> HybridLottieSpec_cxx { + return Unmanaged.fromOpaque(pointer).takeRetainedValue() + } + + /** + * Gets (or creates) the C++ part of this Hybrid Object. + * The C++ part is a `std::shared_ptr`. + */ + public func getCxxPart() -> bridge.std__shared_ptr_HybridLottieSpec_ { + let cachedCxxPart = self.__cxxPart.lock() + if Bool(fromCxx: cachedCxxPart) { + return cachedCxxPart + } else { + let newCxxPart = bridge.create_std__shared_ptr_HybridLottieSpec_(self.toUnsafe()) + __cxxPart = bridge.weakify_std__shared_ptr_HybridLottieSpec_(newCxxPart) + return newCxxPart + } + } + + + + /** + * Get the memory size of the Swift class (plus size of any other allocations) + * so the JS VM can properly track it and garbage-collect the JS object if needed. + */ + @inline(__always) + public var memorySize: Int { + return MemoryHelper.getSizeOf(self.__implementation) + self.__implementation.memorySize + } + + /** + * Call dispose() on the Swift class. + * This _may_ be called manually from JS. + */ + @inline(__always) + public func dispose() { + self.__implementation.dispose() + } + + /** + * Call toString() on the Swift class. + */ + @inline(__always) + public func toString() -> String { + return self.__implementation.toString() + } + + // Properties + public final var url: std.string { + @inline(__always) + get { + return std.string(self.__implementation.url) + } + @inline(__always) + set { + self.__implementation.url = String(newValue) + } + } + + // Methods + public final func getView() -> UnsafeMutableRawPointer { + return Unmanaged.passRetained(__implementation.view).toOpaque() + } + + public final func beforeUpdate() { + __implementation.beforeUpdate() + } + + public final func afterUpdate() { + __implementation.afterUpdate() + } +} diff --git a/native-views/nitrogen/generated/shared/c++/HybridLottieSpec.cpp b/native-views/nitrogen/generated/shared/c++/HybridLottieSpec.cpp new file mode 100644 index 0000000..70f55d9 --- /dev/null +++ b/native-views/nitrogen/generated/shared/c++/HybridLottieSpec.cpp @@ -0,0 +1,22 @@ +/// +/// HybridLottieSpec.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2025 Marc Rousavy @ Margelo +/// + +#include "HybridLottieSpec.hpp" + +namespace margelo::nitro::nativeviews { + + void HybridLottieSpec::loadHybridMethods() { + // load base methods/properties + HybridObject::loadHybridMethods(); + // load custom methods/properties + registerHybrids(this, [](Prototype& prototype) { + prototype.registerHybridGetter("url", &HybridLottieSpec::getUrl); + prototype.registerHybridSetter("url", &HybridLottieSpec::setUrl); + }); + } + +} // namespace margelo::nitro::nativeviews diff --git a/native-views/nitrogen/generated/shared/c++/HybridLottieSpec.hpp b/native-views/nitrogen/generated/shared/c++/HybridLottieSpec.hpp new file mode 100644 index 0000000..9c2af64 --- /dev/null +++ b/native-views/nitrogen/generated/shared/c++/HybridLottieSpec.hpp @@ -0,0 +1,63 @@ +/// +/// HybridLottieSpec.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2025 Marc Rousavy @ Margelo +/// + +#pragma once + +#if __has_include() +#include +#else +#error NitroModules cannot be found! Are you sure you installed NitroModules properly? +#endif + + + +#include + +namespace margelo::nitro::nativeviews { + + using namespace margelo::nitro; + + /** + * An abstract base class for `Lottie` + * Inherit this class to create instances of `HybridLottieSpec` in C++. + * You must explicitly call `HybridObject`'s constructor yourself, because it is virtual. + * @example + * ```cpp + * class HybridLottie: public HybridLottieSpec { + * public: + * HybridLottie(...): HybridObject(TAG) { ... } + * // ... + * }; + * ``` + */ + class HybridLottieSpec: public virtual HybridObject { + public: + // Constructor + explicit HybridLottieSpec(): HybridObject(TAG) { } + + // Destructor + ~HybridLottieSpec() override = default; + + public: + // Properties + virtual std::string getUrl() = 0; + virtual void setUrl(const std::string& url) = 0; + + public: + // Methods + + + protected: + // Hybrid Setup + void loadHybridMethods() override; + + protected: + // Tag for logging + static constexpr auto TAG = "Lottie"; + }; + +} // namespace margelo::nitro::nativeviews diff --git a/native-views/nitrogen/generated/shared/c++/views/HybridLottieComponent.cpp b/native-views/nitrogen/generated/shared/c++/views/HybridLottieComponent.cpp new file mode 100644 index 0000000..dbf8797 --- /dev/null +++ b/native-views/nitrogen/generated/shared/c++/views/HybridLottieComponent.cpp @@ -0,0 +1,87 @@ +/// +/// HybridLottieComponent.cpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2025 Marc Rousavy @ Margelo +/// + +#include "HybridLottieComponent.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace margelo::nitro::nativeviews::views { + + extern const char HybridLottieComponentName[] = "Lottie"; + + HybridLottieProps::HybridLottieProps(const react::PropsParserContext& context, + const HybridLottieProps& sourceProps, + const react::RawProps& rawProps): + react::ViewProps(context, sourceProps, rawProps, filterObjectKeys), + url([&]() -> CachedProp { + try { + const react::RawValue* rawValue = rawProps.at("url", nullptr, nullptr); + if (rawValue == nullptr) return sourceProps.url; + const auto& [runtime, value] = (std::pair)*rawValue; + return CachedProp::fromRawValue(*runtime, value, sourceProps.url); + } catch (const std::exception& exc) { + throw std::runtime_error(std::string("Lottie.url: ") + exc.what()); + } + }()), + hybridRef([&]() -> CachedProp& /* ref */)>>> { + try { + const react::RawValue* rawValue = rawProps.at("hybridRef", nullptr, nullptr); + if (rawValue == nullptr) return sourceProps.hybridRef; + const auto& [runtime, value] = (std::pair)*rawValue; + return CachedProp& /* ref */)>>>::fromRawValue(*runtime, value.asObject(*runtime).getProperty(*runtime, "f"), sourceProps.hybridRef); + } catch (const std::exception& exc) { + throw std::runtime_error(std::string("Lottie.hybridRef: ") + exc.what()); + } + }()) { } + + HybridLottieProps::HybridLottieProps(const HybridLottieProps& other): + react::ViewProps(), + url(other.url), + hybridRef(other.hybridRef) { } + + bool HybridLottieProps::filterObjectKeys(const std::string& propName) { + switch (hashString(propName)) { + case hashString("url"): return true; + case hashString("hybridRef"): return true; + default: return false; + } + } + + HybridLottieComponentDescriptor::HybridLottieComponentDescriptor(const react::ComponentDescriptorParameters& parameters) + : ConcreteComponentDescriptor(parameters, + react::RawPropsParser(/* enableJsiParser */ true)) {} + + std::shared_ptr HybridLottieComponentDescriptor::cloneProps(const react::PropsParserContext& context, + const std::shared_ptr& props, + react::RawProps rawProps) const { + // 1. Prepare raw props parser + rawProps.parse(rawPropsParser_); + // 2. Copy props with Nitro's cached copy constructor + return HybridLottieShadowNode::Props(context, /* & */ rawProps, props); + } + +#ifdef ANDROID + void HybridLottieComponentDescriptor::adopt(react::ShadowNode& shadowNode) const { + // This is called immediately after `ShadowNode` is created, cloned or in progress. + // On Android, we need to wrap props in our state, which gets routed through Java and later unwrapped in JNI/C++. + auto& concreteShadowNode = dynamic_cast(shadowNode); + const HybridLottieProps& props = concreteShadowNode.getConcreteProps(); + HybridLottieState state; + state.setProps(props); + concreteShadowNode.setStateData(std::move(state)); + } +#endif + +} // namespace margelo::nitro::nativeviews::views diff --git a/native-views/nitrogen/generated/shared/c++/views/HybridLottieComponent.hpp b/native-views/nitrogen/generated/shared/c++/views/HybridLottieComponent.hpp new file mode 100644 index 0000000..77f1956 --- /dev/null +++ b/native-views/nitrogen/generated/shared/c++/views/HybridLottieComponent.hpp @@ -0,0 +1,108 @@ +/// +/// HybridLottieComponent.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © 2025 Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "HybridLottieSpec.hpp" +#include +#include + +namespace margelo::nitro::nativeviews::views { + + using namespace facebook; + + /** + * The name of the actual native View. + */ + extern const char HybridLottieComponentName[]; + + /** + * Props for the "Lottie" View. + */ + class HybridLottieProps final: public react::ViewProps { + public: + HybridLottieProps() = default; + HybridLottieProps(const HybridLottieProps&); + HybridLottieProps(const react::PropsParserContext& context, + const HybridLottieProps& sourceProps, + const react::RawProps& rawProps); + + public: + CachedProp url; + CachedProp& /* ref */)>>> hybridRef; + + private: + static bool filterObjectKeys(const std::string& propName); + }; + + /** + * State for the "Lottie" View. + */ + class HybridLottieState final { + public: + HybridLottieState() = default; + + public: + void setProps(const HybridLottieProps& props) { _props.emplace(props); } + const std::optional& getProps() const { return _props; } + + public: +#ifdef ANDROID + HybridLottieState(const HybridLottieState& /* previousState */, folly::dynamic /* data */) {} + folly::dynamic getDynamic() const { + throw std::runtime_error("HybridLottieState does not support folly!"); + } + react::MapBuffer getMapBuffer() const { + throw std::runtime_error("HybridLottieState does not support MapBuffer!"); + }; +#endif + + private: + std::optional _props; + }; + + /** + * The Shadow Node for the "Lottie" View. + */ + using HybridLottieShadowNode = react::ConcreteViewShadowNode; + + /** + * The Component Descriptor for the "Lottie" View. + */ + class HybridLottieComponentDescriptor final: public react::ConcreteComponentDescriptor { + public: + HybridLottieComponentDescriptor(const react::ComponentDescriptorParameters& parameters); + + public: + /** + * A faster path for cloning props - reuses the caching logic from `HybridLottieProps`. + */ + std::shared_ptr cloneProps(const react::PropsParserContext& context, + const std::shared_ptr& props, + react::RawProps rawProps) const override; +#ifdef ANDROID + void adopt(react::ShadowNode& shadowNode) const override; +#endif + }; + + /* The actual view for "Lottie" needs to be implemented in platform-specific code. */ + +} // namespace margelo::nitro::nativeviews::views diff --git a/native-views/nitrogen/generated/shared/json/LottieConfig.json b/native-views/nitrogen/generated/shared/json/LottieConfig.json new file mode 100644 index 0000000..239710e --- /dev/null +++ b/native-views/nitrogen/generated/shared/json/LottieConfig.json @@ -0,0 +1,10 @@ +{ + "uiViewClassName": "Lottie", + "supportsRawText": false, + "bubblingEventTypes": {}, + "directEventTypes": {}, + "validAttributes": { + "url": true, + "hybridRef": true + } +} diff --git a/native-views/src/Lottie.nitro.ts b/native-views/src/Lottie.nitro.ts new file mode 100644 index 0000000..69ecfb7 --- /dev/null +++ b/native-views/src/Lottie.nitro.ts @@ -0,0 +1,8 @@ +import type { HybridView, HybridViewMethods, HybridViewProps } from 'react-native-nitro-modules'; + +export interface LottieProps extends HybridViewProps { + url: string; +} +export interface LottieMethods extends HybridViewMethods {} + +export type Lottie = HybridView; \ No newline at end of file diff --git a/native-views/src/index.tsx b/native-views/src/index.tsx index 7bb7acb..e281540 100644 --- a/native-views/src/index.tsx +++ b/native-views/src/index.tsx @@ -1,11 +1,18 @@ import { getHostComponent } from 'react-native-nitro-modules'; const NativeViewsConfig = require('../nitrogen/generated/shared/json/NativeViewsConfig.json'); +const LottieConfig = require('../nitrogen/generated/shared/json/LottieConfig.json'); import type { NativeViewsMethods, NativeViewsProps, } from './NativeViews.nitro'; +import { LottieMethods, LottieProps } from './Lottie.nitro.ts'; export const NativeViewsView = getHostComponent< NativeViewsProps, NativeViewsMethods >('NativeViews', () => NativeViewsConfig); + +export const LottieView = getHostComponent< + LottieProps, + LottieMethods +>('Lottie', () => LottieConfig) \ No newline at end of file diff --git a/native-views/yarn.lock b/native-views/yarn.lock new file mode 100644 index 0000000..fb57ccd --- /dev/null +++ b/native-views/yarn.lock @@ -0,0 +1,4 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + diff --git a/src/views/HomeScreen/HomeScreenContent.tsx b/src/views/HomeScreen/HomeScreenContent.tsx index 1ceec8f..663b6e0 100644 --- a/src/views/HomeScreen/HomeScreenContent.tsx +++ b/src/views/HomeScreen/HomeScreenContent.tsx @@ -2,7 +2,7 @@ import { Button, Text, View } from 'react-native'; import { HomeScreenViewModel } from './HomeScreenViewModel.ts'; import { observer } from 'mobx-react-lite'; import { multiply } from 'native-modules'; -import { NativeViewsView } from 'native-views'; +import { NativeViewsView, LottieView } from 'native-views'; interface Props { state: Extract; @@ -13,12 +13,15 @@ export const HomeScreenContent = observer(({ state, onButtonPress }: Props) => { return ( Home Screen counter {state.data.counter} - {multiply(2,3)} - -