> For the complete documentation index, see [llms.txt](https://docs.apryse.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.apryse.com/ios/get-started/frameworks/react-native/add-an-api.md).

# Add an API for React Native

Learn how to implement React Native modules in iOS, from defining properties to sending events to JS. Follow step-by-step instructions for seamless integration. The Apryse iOS SDK streamlines secure d

## Introduction

The React Native API for Apryse Mobile SDK includes all of the most used functions and methods for viewing, annotating and saving PDF documents. However, it is possible your app may need access to APIs that are available as part of the native API, but are not directly available to React Native.

This guide provides an example of how to add the following to the React Native interface:

* `pageChangeOnTap` prop that determines whether page will turn left or right when tapping corresponding edges.
* `getField` function which retrieves information about a field using its name.
* `onLayoutChanged` event listener that is raised when the layout of viewer has changed.

You can follow the same pattern to add new functions and props that your React Native app may need. The new additions could be simple ones, which expose one piece of functionality, or custom ones, that expose a series of native commands under the hood.

Prior to following this guide, we highly recommend you to go through the official guide here: [Native UI Components](https://reactnative.dev/docs/native-components-android/) to have a better understanding of the system.

### 1. Fork and clone Apryse's React Native Repo

The source is hosted on GitHub here: [https://github.com/ApryseSDK/pdftron-react-native](https://github.com/ApryseSDK/pdftron-react-native/)

Fork the project and clone a copy of the repository to your disk.

## Adding the `pageChangeOnTap` prop

### 2. Add prop to DocumentView\.tsx interface file

The `DocumentView.tsx` interface file lists all of the React Native props on the DocumentView component.

Add the prop declaration:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
static propTypes = {
  pageChangeOnTap: PropTypes.bool,
};
```

{% endcode %}
{% endtab %}
{% endtabs %}

For props that accept `Config` constants or arrays of `Config` constants, use the `oneOf` and `arrayOf` helper methods. These helpers return flexible types that allow custom checks for TypeScript users, while maintaining standard run-time checks for all users.

### 3. Define a ReactProp matches the TS declaration

Open file `ios/RNTPTDocumentViewManager.m`.

Add the method key that matches the TS declaration:

{% tabs %}
{% tab title="Obj-C" %}
{% code lineNumbers="true" %}

```objc
RCT_CUSTOM_VIEW_PROPERTY(pageChangeOnTap, BOOL, RNTPTDocumentView)
{
    if (json) {
        view.pageChangeOnTap = [RCTConvert BOOL:json];
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

### 4. Implement the methods to set the property

Open the `ios/RNTPTDocumentView.h` interface file and add a new property to the `RNTPTDocumentView` class:

{% tabs %}
{% tab title="Obj-C" %}
{% code lineNumbers="true" %}

```objc
@interface RNTPTDocumentView

...

@property (nonatomic, assign) BOOL pageChangeOnTap;

...

@end
```

{% endcode %}
{% endtab %}
{% endtabs %}

Open the `ios/RNTPTDocumentView.m` implementation file, and add the property setter:

{% tabs %}
{% tab title="Obj-C" %}
{% code lineNumbers="true" %}

```objc
- (void)setPageChangeOnTap:(BOOL)pageChangeOnTap
{
    _pageChangeOnTap = pageChangeOnTap;
    
    if (self.documentViewController) {
        [self applyViewerSettings];
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

In this case, we call the `applyViewerSettings` method and access the `changesPageOnTap` property to complete our implementation:

{% tabs %}
{% tab title="Obj-C" %}
{% code lineNumbers="true" %}

```objc
- (void)applyViewerSettings
{
	...

    // Page change on tap.
    self.documentViewController.changesPageOnTap = self.pageChangeOnTap;

	...
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

The actual implementation will depend on the actual functionality.

## Adding the `getField` function

### 2. Add function to DocumentView\.tsx interface file

The `DocumentView.tsx` interface file lists all of the React Native functions on the DocumentView component.

Add the function declaration:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
getField = (fieldName: string): Promise<void | {fieldName: string, fieldValue?: any, fieldType?: string}> => {
    const tag = findNodeHandle(this._viewerRef);
    if(tag != null) {
      return DocumentViewManager.getField(tag, fieldName);
    }
    return Promise.resolve();
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

All functions return `Promise<void | T>` where `T` is the expected return type upon success. If the function returns `void` upon success, simply put `Promise<void>`.

While you are adding types, consider representing objects with reusable object types. You can use existing ones, or create a new type alias or interface in `AnnotOptions.ts` (see [Object Types](https://www.typescriptlang.org/docs/handbook/2/objects.html)).

### 3. Add method to module

The JS name of the new method is `getField` and the native method is `getFieldForDocumentViewTag:fieldname:`.

In `ios/RNTPTDocumentViewModule.m`, use the `RCT_REMAP_METHOD` macro to expose the native method to JS and simultaneously specify the JS name of the method.

{% tabs %}
{% tab title="Obj-C" %}
{% code lineNumbers="true" %}

```objc
RCT_REMAP_METHOD(getField,
                 getFieldForDocumentViewTag:(nonnull NSNumber *)tag
                 fieldName:(NSString *)fieldName
                 resolver:(RCTPromiseResolveBlock)resolve
                 rejecter:(RCTPromiseRejectBlock)reject)
{
    @try {
        NSDictionary *field = [[self documentViewManager] getFieldForDocumentViewTag:tag fieldName:fieldName];
        resolve(field);
    }
    @catch (NSException *exception) {
        reject(@"set_value_for_fields", @"Failed to set value on fields", [self errorFromException:exception]);
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

### 4. Implement the native method

Add the native method to `ios/RNTPTDocumentViewManager.h`:

{% tabs %}
{% tab title="Obj-C" %}
{% code lineNumbers="true" %}

```objc
- (NSDictionary *)getFieldForDocumentViewTag:(NSNumber *)tag fieldName:(NSString *)fieldName;
```

{% endcode %}
{% endtab %}
{% endtabs %}

Implement the native method in `ios/RNTPTDocumentViewManager.m`:

{% tabs %}
{% tab title="Obj-C" %}
{% code lineNumbers="true" %}

```objc
- (NSDictionary *)getFieldForDocumentViewTag:(NSNumber *)tag fieldName:(NSString *)fieldName
{
    RNTPTDocumentView *documentView = self.documentViews[tag];
    if (documentView) {
        return [documentView getField:fieldName];
    } else {
        @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Unable to get field for tag" userInfo:nil];
        return nil;
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

### 5. Implement the helper method

Add the following to `ios/RNTPTDocumentView.h`:

{% tabs %}
{% tab title="Obj-C" %}
{% code lineNumbers="true" %}

```objc
- (NSDictionary *)getField:(NSString *)fieldName;
```

{% endcode %}
{% endtab %}
{% endtabs %}

Implement the method in `ios/RNTPTDocumentView.m`:

{% tabs %}
{% tab title="Obj-C" %}
{% code lineNumbers="true" %}

```objc
- (NSDictionary *)getField:(NSString *)fieldName
{
    PTPDFViewCtrl *pdfViewCtrl = self.currentDocumentViewController.pdfViewCtrl;
    if (!pdfViewCtrl) {
        return nil;
    }
    
    NSMutableDictionary <NSString *, NSObject *> *fieldMap = [[NSMutableDictionary alloc] init];

    NSError *error;
    [pdfViewCtrl DocLockReadWithBlock:^(PTPDFDoc * _Nullable doc) {
        
        PTField *field = [doc GetField:fieldName];
        if (field && [field IsValid]) {
            
            PTFieldType fieldType = [field GetType];
            NSString* typeString;
            if (fieldType == e_ptbutton) {
                typeString = PTFieldTypeButtonKey;
            } else if (fieldType == e_ptcheck) {
                typeString = PTFieldTypeCheckboxKey;
                [fieldMap setValue:[[NSNumber alloc] initWithBool:[field GetValueAsBool]] forKey:PTFormFieldValueKey];
            } else if (fieldType == e_ptradio) {
                typeString = PTFieldTypeRadioKey;
                [fieldMap setValue:[field GetValueAsString] forKey:PTFormFieldValueKey];
            } else if (fieldType == e_pttext) {
                typeString = PTFieldTypeTextKey;
                [fieldMap setValue:[field GetValueAsString] forKey:PTFormFieldValueKey];
            } else if (fieldType == e_ptchoice) {
                typeString = PTFieldTypeChoiceKey;
                [fieldMap setValue:[field GetValueAsString] forKey:PTFormFieldValueKey];
            } else if (fieldType == e_ptsignature) {
                typeString = PTFieldTypeSignatureKey;
            } else {
                typeString = PTFieldTypeUnknownKey;
            }
            
            [fieldMap setValue:typeString forKey:PTFormFieldTypeKey];
            [fieldMap setValue:fieldName forKey:PTFormFieldNameKey];
        }
            
        
    } error:&error];
    
    if (error) {
        NSLog(@"Error: There was an error while trying to get field. %@", error.localizedDescription);
    }
    
    return [[fieldMap allKeys] count] == 0 ? nil : fieldMap;
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

The logic is to get the PDF view controller and if the field names are valid, use the controller to get the fields from the current document.

## Adding the `onLayoutChanged` event listener

### 2. Define the new listener in TS

Event listeners are handled in `DocumentView.tsx`. Add the following to the file:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
static propTypes = {
    ...
    onLayoutChanged: func<() => void>(),
}

onChange = (event) => {
    if (event.nativeEvent.onLeadingNavButtonPressed) {
        ...
    } else if (event.nativeEvent.onLayoutChanged) {
      if (this.props.onLayoutChanged) {
        this.props.onLayoutChanged();
      }
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

The `onLayoutChanged` event listener does not require any arguments. If your event listener requires arguments, access them using `event.nativeEvent.exampleArg`.

For event listeners and other props that accept functions, use the `func` helper method. This helper returns flexible types that allow custom checks for TypeScript users, while maintaining standard run-time checks for all users.

While you are adding types, consider representing objects with reusable object types. You can use existing ones, or create a new type alias or interface in `AnnotOptions.ts` (see [Object Types](https://www.typescriptlang.org/docs/handbook/2/objects.html)).

### 3. Send event to JS

Add the following protocol method to `ios/RNTPTDocumentView.h`:

{% tabs %}
{% tab title="Obj-C" %}
{% code lineNumbers="true" %}

```objc
@protocol RNTPTDocumentViewDelegate <NSObject>
@optional
...
- (void)layoutChanged:(RNTPTDocumentView *)sender; 
...
@end
```

{% endcode %}
{% endtab %}
{% endtabs %}

Implement the protocol method in `ios/RNTPTDocumentViewManager.m`, under the Events pragma mark. Notice that we call `RNTPTDocumentView`'s `RCTBubblingEventBlock onChange` event handler block, which will call the callback prop in JS.

{% tabs %}
{% tab title="Obj-C" %}
{% code lineNumbers="true" %}

```objc
#pragma mark - Events
...
- (void)layoutChanged:(RNTPTDocumentView *)sender
{
    if (sender.onChange) {
        sender.onChange(@{
            @"onLayoutChanged" : @"onLayoutChanged",
        });
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

### 4. Call `layoutChanged` protocol method in iOS

In step 3, we implemented the protocol method `layoutChanged` to send events to JS, but we haven't used it yet.

Add the following protocol method to `ios/DocumentViewController/RNTPTDocumentViewController.h`:

{% tabs %}
{% tab title="Obj-C" %}
{% code lineNumbers="true" %}

```objc
@protocol RNTPTDocumentBaseViewControllerDelegate <PTToolManagerDelegate>
@required
...
- (void)rnt_documentViewControllerLayoutDidChange:(PTDocumentBaseViewController *)documentViewController;
...
@end
```

{% endcode %}
{% endtab %}
{% endtabs %}

Implement this protocol method in `ios/RNTPTDocumentView.m`, under the RNTPTDocumentViewControllerDelegate pragma mark. This is where we use `layoutChanged`.

{% tabs %}
{% tab title="Obj-C" %}
{% code lineNumbers="true" %}

```objc
#pragma mark - <RNTPTDocumentViewControllerDelegate>
...
- (void)rnt_documentViewControllerLayoutDidChange:(PTDocumentBaseViewController *)documentViewController
{
    if ([self.delegate respondsToSelector:@selector(layoutChanged:)]) {
        [self.delegate layoutChanged:self];
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

### 5. Receive layout changes from PDFViewCtrl

The `PTPDFViewCtrlDelegate` protocol contains the method `pdfViewCtrlOnLayoutChanged`. This method allows adopting delegates to respond to the `PTPDFViewCtrl` class when page layout has changed.

Implement `pdfViewCtrlOnLayoutChanged` in the following files:

* `ios/DocumentViewController/RNTPTDocumentViewController.m`
* `ios/DocumentViewController/RNTPTDocumentController.m`
* `ios/DocumentViewController/RNTPTCollaborationDocumentViewController.m`
* `ios/DocumentViewController/RNTPTCollaborationDocumentController.m`

{% tabs %}
{% tab title="Obj-C" %}
{% code lineNumbers="true" %}

```objc
#pragma mark - <PTPDFViewCtrlDelegate>
...
- (void)pdfViewCtrlOnLayoutChanged:(PTPDFViewCtrl *)pdfViewCtrl
{
    if ([self.delegate respondsToSelector:@selector(rnt_documentViewControllerLayoutDidChange:)]) {
        [self.delegate rnt_documentViewControllerLayoutDidChange:self];
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

The actual implementation will depend on the actual functionality.

## Finishing steps

### 1. Push the code and integrate the updated source

Now `npm install` your forked version to your application.

The new functionality is now ready to use.

### 2. Access the new functionality

The app can now access the new API as follows:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
onDocumentLoaded = () => {
    this._viewer.getField('someFieldName').then((field) => {
        if (field !== undefined) {
            console.log('field name:', field.fieldName);
            console.log('field value:', field.fieldValue);
            console.log('field type:', field.fieldType);
        }
    });
}

onLayoutChanged = () => {
    console.log("Layout changed");
}

<DocumentView
    ref={(c) => this._viewer = c}
    pageChangeOnTap={false}
    onDocumentLoaded={this.onDocumentLoaded}
    onLayoutChanged={this.onLayoutChanged}
    document={this.state.document}
/>
```

{% endcode %}
{% endtab %}
{% endtabs %}

### 3. All done!

If you're only developing for iOS, then you're all done!

If you're also deploying on Android, you'll need to complete the necessary steps for Android.

If you're developing for both iOS and Android, please consider [submitting a PR](https://github.com/ApryseSDK/pdftron-react-native/blob/master/CONTRIBUTING.md), as upstreaming the change will simplify your developing and make the APIs available for other Apryse customers.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.apryse.com/ios/get-started/frameworks/react-native/add-an-api.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
