Creating You.i Engine One Native Components with AE Workflow

John Cassidy
John Cassidy

What is a Native Component?

A Native Component is an implementation of a Shadow View (read refresh on what Shadow Views are here). It contains a yoga node that is used in React Native JSX (the JSX component) and creates a counterpart view that is an Element understood by You.Engine One. It is responsible for mapping the properties of one to the other, so when you work with the yoga node in JSX, the Element within You.i Engine One that is rendered is updated accordingly.

Native Component_shadow view

Why would we need our own Native Component?

If your project uses the After Effects Workflow in order to create compositions and export them for use within React Native JSX, you may note that the number of available Components available for use is smaller than the number of Classes supported by You.i Engine One SDK.

The most commonly used Native Components made available in @youi/react-native-youi cover most of the use cases you may encounter when building an application with the AE Workflow, but there are situations where your application could benefit from an SDK Element that is not available in the RN layer.

Building a Native Component for a You.i Engine Component that is not currently exposed to RN

A good example of a specialized class found in the engine that is not exposed to React Native is CYIScrollingTextView. This is a class whose functionality is to present a way of showing large amounts of text in a stylized fashion (with the design work being done in After Effects) while taking advantage of the streaming capabilities of CYIListView under the hood.

Native Component_scrolling text

CYIScrollingText Native Component Implementation

In order to interact with this component in a simple way familiar to React Native developers, a custom Native Component allows the creation of a JSX Component (yoga node) and definition of properties for a specific use case. In this case, a template is provided to be used for the list items (each paragraph of text is represented by a list item) and the text to be set.

import React, { Component } from 'react';
import { AppRegistry } from 'react-native';
import { Composition } from '@youi/react-native-youi';
import NativeScrollingTextViewRef from './NativeScrollingTextView';

const TEXT = `
 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Integer commodo. Donec cursus nunc non velit. Maecenas sollicitudin ligula nec magna. Nullam vestibulum magna ac urna. Cras non magna vitae tortor imperdiet dapibus. Etiam elementum, leo vel pretium mattis, libero erat interdum nibh, eu consectetuer turpis mauris eu pede.\n\n
Curabitur leo. Proin pede dui, euismod at, aliquet a, feugiat at, ante. Curabitur id dui nec elit fringilla ornare. Suspendisse quam. Nullam tortor. \n\n
Etiam non enim. Donec blandit, arcu non sagittis accumsan, tortor elit aliquam dolor, nec tempus tellus massa semper dui. Mauris enim odio, facilisis nec, accumsan id, porta vitae, leo. Aliquam auctor neque quis erat. Nam aliquam eros semper quam. Donec luctus erat vitae wisi. Cras mauris sem, cursus sit amet, molestie sed, vestibulum sit amet, lectus. Nunc ac arcu semper elit pharetra blandit. Nullam vestibulum rutrum dui. Mauris risus. \n\n
Pellentesque gravida. Nullam ullamcorper consequat sem. Nunc neque libero, consequat vel, iaculis ut, nonummy vitae, justo. Sed dignissim. Nam nec risus. Sed vitae nulla. \n\n
Vestibulum eros. Nullam tempus. Vestibulum nisl nunc, sollicitudin eget, hendrerit semper, laoreet a, augue. In sed sapien et lectus ultrices tempus. Nam egestas libero nec odio. Fusce tristique ipsum id lacus. \n\n
Duis purus. Curabitur enim. Aliquam erat volutpat. Aliquam nonummy bibendum sapien. Curabitur porta rutrum tellus. Maecenas mi augue, consequat et, tincidunt non, adipiscing vel, felis. Phasellus vel odio a arcu malesuada ornare. Proin dapibus. 
Praesent tincidunt. Nullam facilisis. Donec vulputate elementum tortor. Quisque nec diam. Sed pretium turpis in mauris. Pellentesque dignissim sem nec nulla. Aliquam leo ipsum, congue ac, porttitor semper, semper semper, sapien. Maecenas viverra, mauris quis hendrerit rutrum, tellus lorem condimentum arcu, semper rutrum libero odio sed mauris. Suspendisse potenti. Mauris ante lectus, vestibulum ac, hendrerit et, congue vel, enim. Mauris gravida adipiscing quam. \n\n
Maecenas ac massa. Nunc non lacus id urna vulputate rhoncus. Nunc euismod. Vestibulum posuere lacinia felis. Suspendisse pretium egestas orci. Vivamus tempus. \n\n
Etiam elit sem, vestibulum sit amet, volutpat vitae, viverra eget, mauris. Aliquam nunc ipsum, interdum quis, consectetuer in, consectetuer sit amet, lorem. Mauris elit. In hac habitasse platea dictumst. Morbi libero velit, hendrerit ut, posuere a, eleifend semper, odio. Aliquam erat volutpat. Mauris auctor turpis sit amet diam. \n\n
Vivamus dolor nibh, varius semper, ultrices in, porta at, libero. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Cras vehicula ante sed eros. Aliquam vestibulum commodo pede. Proin eleifend posuere odio. Quisque eros. Curabitur egestas diam a risus. Nam felis. \n\n
Nullam eu justo. Nullam faucibus consequat sem. Duis vel tellus a ligula tristique malesuada. Curabitur aliquet. Nullam dignissim. Aenean non nulla sed lacus ultrices consequat. Curabitur felis. Ut tincidunt pretium felis. In vulputate magna ut eros. Quisque lorem lectus, lobortis nec, nonummy vel, vehicula at, arcu. Donec nunc. Etiam imperdiet. Etiam sollicitudin turpis in quam. Quisque laoreet interdum sem. Vestibulum orci. Pellentesque sapien nunc, volutpat ut, bibendum eget, tincidunt semper, elit. \n\n
Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Phasellus pede. Nulla vel erat. Quisque massa augue, cursus a, sodales et, condimentum a, pede. Vestibulum vel diam. Fusce eros ipsum, dignissim id, vestibulum in, semper sit amet, justo. Etiam metus risus, elementum vel, varius sed, luctus at, neque. \n\n
Vestibulum sapien. Suspendisse placerat pulvinar lacus. In tellus arcu, mollis sed, faucibus vel, ullamcorper quis, tellus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis ipsum. Nam urna. Nulla libero leo, dignissim at, vulputate ut, iaculis vel, est. Praesent at ipsum. Nam auctor massa quis nulla. Fusce enim sem, fringilla vel, convallis at, tincidunt vel, pede. Maecenas dolor.\n\n`;

export default class YiReactApp extends Component {
  render() {
    return (
      <Composition source="App_ScrollingTextViewContainer">
        <NativeScrollingTextViewRef template="App_Text-Container" name="Scrolling-Text" text={TEXT} />
      </Composition>
    );
  }
}

AppRegistry.registerComponent('YiReactApp', () => YiReactApp);

Creating our Template in After Effects

For the You.i Engine One CYIListView Element that will be linked to the above JSX Component, an After Effects template will be required to construct it. Looking at the header file for YiScrollingTextView.h, the recommended structure of the After Effects Composition is revealed.

Native Component_placeholder

At the root is a simple container with a CYIScrollingTextView child.

This parent container is a requirement for any Native Component. It is what will be presented as the source for a <Composition /> Component and it will be the root of the scene tree that will be referenced by the Native Component within.

This holds true for any of the AE Workflow components. If you want to use the power of the particular type (ButtonRef,ImageRef,ViewRef), they need to be wrapped in a container to act as the source of the scene tree.

<Composition source="Source_ParentContainer">
  <ButtonRef name="ChildName" onPress={ () => ... } />
</Composition>

The main root composition is what is going to wrap the CYIScrollingTextView Native Component.

Native Component_composition_1

Main Composition that contains our CYIScrollingTextView

The CYIScrollingTextView child layer of the root container has a few layers itself. The first is the visual representation of the list, in this case the solid blue layer that will represent the scroll bar. The second layer present, named Text-Container, is the template that will represent a single paragraph of text. The name is not important, but it needs to be tracked as it will be referenced explicitly as a prop to the Native Component.

Native Component_composition_2

CYIScrollingTextView custom class implementation

Within the Text-Container layer is placeholder text that will render the list item contents (the paragraph that is using this list item).

Native Component_composition_3

A CYIScrollingTextView element from You.i Engine One can now be constructed using the After Effects template described above. As mentioned, the root container will be used as the composition source of the Native Component.

<Composition source="File_ScrollingTextViewContainer">
  { // create JSX Component to represent Native Component here }</Composition>

Creating a JSX Component

Within that Composition, a JSX Component will be used to represent a Native Component. The properties that we provide this JSX Component are what will be linked to our You.i Engine One Element.

We can create a JSX Component mock structure that will represent our needs pretty quickly:

<Composition source="File_ScrollingTextViewContainer">
  <NativeScrollingTextView
    name="Scrolling-Text"
    template="File_Text-Container"
    text={text_to_display} 
  />
</Composition>

name represents the layer name for the CYIScrollingTextView composition in the After Effects template, the child of the root container.

template represents the source of the list item to be used for each paragraph of text, Text-Container in the above sample.

text represents the large chunk of text to be displayed.

Creating a Native Shadow View

Before this JSX Component can be created, a representative Native Component must exist to receive the properties, create the appropriate CYIScrollingListView Counterpart, and map the above mocked properties to the You.i Engine One Element.

The above NativeScrollingTextView is actually a Native Component by the name of ShadowScrollingTextView.

const NativeScrollingTextView = requireNativeComponent("ShadowScrollingTextView");
<Composition source="File_ScrollingTextViewContainer">
  <NativeScrollingTextView
    name="Scrolling-Text"
    template="File_Text-Container"
    text={text_to_display} 
  />
</Composition>

The Native Component is represented by two parts:

1) Implementation of ShadowViewRef

The ShadowViewRef implementation is what is surfaced to JSX. This is the Native Component that is registered as a view module and has a friendly name exported, ShadowScrollingTextView, that is used in the above JSX implementation.

class ShadowScrollingTextViewRef : public ShadowViewRef
{
public:
    
    ShadowScrollingTextViewRef();
    virtual ~ShadowScrollingTextViewRef() final;
    YI_RN_EXPORT_NAME(ShadowScrollingTextView);
    YI_RN_DECLARE_MANAGER(ScrollingTextViewManagerModule);

    virtual const CYIScrollingTextView *GetCounterpart() const override;
    virtual CYIScrollingTextView *GetCounterpart() override;

private:
    virtual CYIScrollingTextView *FindCounterpartNode(CYISceneView &parentView) const override;

    YI_TYPE_BASES(ShadowScrollingTextViewRef, ShadowViewRef);
};

It provides implementations to find the appropriate node from the scene tree that represents the expected counterpart CYIScrollingTextView (the You.i Engine One Element). As this is a Shadow View for an After Effects composition, the node is not created programmatically, but instead, the node is found in the existing scene tree from the root composition source. Remember that this Native Component is wrapped in a Composition, and a name was provided to reference it within the scene tree, Scrolling-Text.

const CYIScrollingTextView *ShadowScrollingTextViewRef::GetCounterpart() const
{
    return static_cast<const CYIScrollingTextView *>(ShadowViewRef::GetCounterpart());
}

CYIScrollingTextView *ShadowScrollingTextViewRef::GetCounterpart()
{
    return static_cast<CYIScrollingTextView *>(ShadowViewRef::GetCounterpart());
}

CYIScrollingTextView *ShadowScrollingTextViewRef::FindCounterpartNode(CYISceneView &parentView) const
{
    CYIScrollingTextView *pScrollingTextView = nullptr;
    parentView.FindNode(pScrollingTextView, GetName(), CYISceneView::FetchType::Optional, TAG);
    return pScrollingTextView;
}

See the You.i developer documentation on CYISceneNode for more information on the above snippet

To manage the properties and mapping of properties from the JSX node to the counterpart view, it declares a Module Manager to represent it’s interests.

2) Implementation of an Abstract-Component-Manager-Module

 

class YI_RN_MODULE(ScrollingTextViewManagerModule, AbstractComponentManagerModule), public IViewManager
{
public:
    YI_RN_EXPORT_NAME(ScrollingTextViewManager);

    YI_RN_DEFINE_COMPONENT_MODULE();

protected:
    virtual void SetupProperties() override;
};

The Module Manager is responsible for defining, receiving, and setting properties from the JSX node and applying them to the appropriate counterpart provided by the ShadowViewRef implementation.

Properties that a Native Component receives are defined with an appropriate type.

folly::dynamic ScrollingTextViewManagerModule::GetNativeProps()
{
    folly::dynamic superProps = IViewManager::GetNativeProps();
    folly::dynamic props = folly::dynamic::object("text", "string")("template", "string")("testID", "string");
    return folly::dynamic::merge(superProps, props);
}

This is important as it allows the exact definition of properties to be received as they will be applied to the CYIScrollingTextView counterpart. In the same Manager Module implementation, we will provide the method to set these properties to the counterpart You.i Engine One Element.

void ScrollingTextViewManagerModule::SetupProperties()
{
    IViewManager::SetupProperties();
    
    YI_RN_DEFINE_PROPERTY("template", [](ShadowScrollingTextViewRef &self, CYIString assetTemplate) {
       self.GetCounterpart()->SetListItemTemplateName(assetTemplate);
   });
    
    YI_RN_DEFINE_PROPERTY("text", [](ShadowScrollingTextViewRef &self, CYIString text) {
        self.GetCounterpart()->SetText(std::move(text));
    });
}

The counterpart has now be populated with properties that are provided to the Native Components JSX Component (yoga node). We can use this JSX Component in our React Native app, within a Composition that contains it.

import React from 'react';
import { requireNativeComponent } from 'react-native';

const NativeScrollingTextView = requireNativeComponent("NativeScrollingTextView");

export default class NativeScrollingTextViewRef extends React.PureComponent {
  render() {
    return <NativeScrollingTextView {...this.props} />;
  }
}
import React, { Component } from 'react';
import { AppRegistry } from 'react-native';
import { Composition } from '@youi/react-native-youi';
import NativeScrollingTextViewRef from './NativeScrollingTextView';

const TEXT = `
 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Integer commodo. Donec cursus nunc non velit. Maecenas sollicitudin ligula nec magna. Nullam vestibulum magna ac urna. Cras non magna vitae tortor imperdiet dapibus. Etiam elementum, leo vel pretium mattis, libero erat interdum nibh, eu consectetuer turpis mauris eu pede.\n\n
Curabitur leo. Proin pede dui, euismod at, aliquet a, feugiat at, ante. Curabitur id dui nec elit fringilla ornare. Suspendisse quam. Nullam tortor. \n\n
Etiam non enim. Donec blandit, arcu non sagittis accumsan, tortor elit aliquam dolor, nec tempus tellus massa semper dui. Mauris enim odio, facilisis nec, accumsan id, porta vitae, leo. Aliquam auctor neque quis erat. Nam aliquam eros semper quam. Donec luctus erat vitae wisi. Cras mauris sem, cursus sit amet, molestie sed, vestibulum sit amet, lectus. Nunc ac arcu semper elit pharetra blandit. Nullam vestibulum rutrum dui. Mauris risus. \n\n
Pellentesque gravida. Nullam ullamcorper consequat sem. Nunc neque libero, consequat vel, iaculis ut, nonummy vitae, justo. Sed dignissim. Nam nec risus. Sed vitae nulla. \n\n
Vestibulum eros. Nullam tempus. Vestibulum nisl nunc, sollicitudin eget, hendrerit semper, laoreet a, augue. In sed sapien et lectus ultrices tempus. Nam egestas libero nec odio. Fusce tristique ipsum id lacus. \n\n
Duis purus. Curabitur enim. Aliquam erat volutpat. Aliquam nonummy bibendum sapien. Curabitur porta rutrum tellus. Maecenas mi augue, consequat et, tincidunt non, adipiscing vel, felis. Phasellus vel odio a arcu malesuada ornare. Proin dapibus. 
Praesent tincidunt. Nullam facilisis. Donec vulputate elementum tortor. Quisque nec diam. Sed pretium turpis in mauris. Pellentesque dignissim sem nec nulla. Aliquam leo ipsum, congue ac, porttitor semper, semper semper, sapien. Maecenas viverra, mauris quis hendrerit rutrum, tellus lorem condimentum arcu, semper rutrum libero odio sed mauris. Suspendisse potenti. Mauris ante lectus, vestibulum ac, hendrerit et, congue vel, enim. Mauris gravida adipiscing quam. \n\n
Maecenas ac massa. Nunc non lacus id urna vulputate rhoncus. Nunc euismod. Vestibulum posuere lacinia felis. Suspendisse pretium egestas orci. Vivamus tempus. \n\n
Etiam elit sem, vestibulum sit amet, volutpat vitae, viverra eget, mauris. Aliquam nunc ipsum, interdum quis, consectetuer in, consectetuer sit amet, lorem. Mauris elit. In hac habitasse platea dictumst. Morbi libero velit, hendrerit ut, posuere a, eleifend semper, odio. Aliquam erat volutpat. Mauris auctor turpis sit amet diam. \n\n
Vivamus dolor nibh, varius semper, ultrices in, porta at, libero. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Cras vehicula ante sed eros. Aliquam vestibulum commodo pede. Proin eleifend posuere odio. Quisque eros. Curabitur egestas diam a risus. Nam felis. \n\n
Nullam eu justo. Nullam faucibus consequat sem. Duis vel tellus a ligula tristique malesuada. Curabitur aliquet. Nullam dignissim. Aenean non nulla sed lacus ultrices consequat. Curabitur felis. Ut tincidunt pretium felis. In vulputate magna ut eros. Quisque lorem lectus, lobortis nec, nonummy vel, vehicula at, arcu. Donec nunc. Etiam imperdiet. Etiam sollicitudin turpis in quam. Quisque laoreet interdum sem. Vestibulum orci. Pellentesque sapien nunc, volutpat ut, bibendum eget, tincidunt semper, elit. \n\n
Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Phasellus pede. Nulla vel erat. Quisque massa augue, cursus a, sodales et, condimentum a, pede. Vestibulum vel diam. Fusce eros ipsum, dignissim id, vestibulum in, semper sit amet, justo. Etiam metus risus, elementum vel, varius sed, luctus at, neque. \n\n
Vestibulum sapien. Suspendisse placerat pulvinar lacus. In tellus arcu, mollis sed, faucibus vel, ullamcorper quis, tellus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis ipsum. Nam urna. Nulla libero leo, dignissim at, vulputate ut, iaculis vel, est. Praesent at ipsum. Nam auctor massa quis nulla. Fusce enim sem, fringilla vel, convallis at, tincidunt vel, pede. Maecenas dolor.\n\n`;

export default class YiReactApp extends Component {
  render() {
    return (
      <Composition source="App_ScrollingTextViewContainer">
        <NativeScrollingTextViewRef template="App_Scrolling-Text" name="Scrolling-Text" text={TEXT} />
      </Composition>
    );
  }
}

AppRegistry.registerComponent('YiReactApp', () => YiReactApp);

From this implementation, there are avenues where we can go beyond the basic implementation of a counterpart view, for example adding a ScrollListener to the counterpart to listen for when the end of the list is reached so we can emit a signal to our JSX component to react to.

If You.i Engine One SDK has a component that you want to access in your React Native application code, it’s possible with a custom Native Module.

Here’s something similar we think you’ll enjoy.