Fabric Native Components: iOS
Now it's time to write some iOS platform code to be able to render the web view. The steps you need to follow are:
- Run Codegen.
- Write the code for the
RCTWebView
- Register the
RCTWebView
in the application
1. Run Codegen
You can manually run the Codegen, however it's simpler to use the application you're going to demo the component in to do this for you.
cd ios
bundle install
bundle exec pods install
Importantly you will see logging output from Codegen, which we're going to use in Xcode to build our WebView native component.
You should be careful about committing generated code to your repository. Generated code is specific to each version of React Native. Use npm peerDependencies to restrict compatibility with version of React Native.
3. Write the RCTWebView
We need to prepare your iOS project using Xcode by completing these 5 steps:
- Open the CocoPods generated Xcode Workspace:
cd ios
open Demo.xcworkspace

- Right click on app and select
New Group
, call the new groupWebView
.

- In the
WebView
group, createNew
→File from Template
.

- Use the
Objective-C File
template, and name itRCTWebView
.

- Rename
RCTWebView.m
→RCTWebView.mm
making it an Objective-C++ file
Podfile
...
Demo
├── AppDelegate.h
├── AppDelegate.mm
...
├── RCTWebView.h
├── RCTWebView.mm
└── main.m
After creating the header file and the implementation file, you can start implementing them.
This is the code for the RCTWebView.h
file, which declares the component interface.
#import <React/RCTViewComponentView.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface RCTWebView : RCTViewComponentView
// You would declare native methods you'd want to access from the view here
@end
NS_ASSUME_NONNULL_END
This class defines an RCTWebView
which extends the RCTViewComponentView
class. This is the base class for all the native components and it is provided by React Native.
The code for the implementation file (RCTWebView.mm
) is the following:
#import "RCTWebView.h"
#import <react/renderer/components/AppSpecs/ComponentDescriptors.h>
#import <react/renderer/components/AppSpecs/EventEmitters.h>
#import <react/renderer/components/AppSpecs/Props.h>
#import <react/renderer/components/AppSpecs/RCTComponentViewHelpers.h>
#import <WebKit/WebKit.h>
using namespace facebook::react;
@interface RCTWebView () <RCTCustomWebViewViewProtocol, WKNavigationDelegate>
@end
@implementation RCTWebView {
NSURL * _sourceURL;
WKWebView * _webView;
}
-(instancetype)init
{
if(self = [super init]) {
_webView = [WKWebView new];
_webView.navigationDelegate = self;
[self addSubview:_webView];
}
return self;
}
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
const auto &oldViewProps = *std::static_pointer_cast<CustomWebViewProps const>(_props);
const auto &newViewProps = *std::static_pointer_cast<CustomWebViewProps const>(props);
// Handle your props here
if (oldViewProps.sourceURL != newViewProps.sourceURL) {
NSString *urlString = [NSString stringWithCString:newViewProps.sourceURL.c_str() encoding:NSUTF8StringEncoding];
_sourceURL = [NSURL URLWithString:urlString];
if ([self urlIsValid:newViewProps.sourceURL]) {
[_webView loadRequest:[NSURLRequest requestWithURL:_sourceURL]];
}
}
[super updateProps:props oldProps:oldProps];
}
-(void)layoutSubviews
{
[super layoutSubviews];
_webView.frame = self.bounds;
}
#pragma mark - WKNavigationDelegate
-(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
{
CustomWebViewEventEmitter::OnScriptLoaded result = CustomWebViewEventEmitter::OnScriptLoaded{CustomWebViewEventEmitter::OnScriptLoadedResult::Success};
self.eventEmitter.onScriptLoaded(result);
}
- (BOOL)urlIsValid:(std::string)propString
{
if (propString.length() > 0 && !_sourceURL) {
CustomWebViewEventEmitter::OnScriptLoaded result = CustomWebViewEventEmitter::OnScriptLoaded{CustomWebViewEventEmitter::OnScriptLoadedResult::Error};
self.eventEmitter.onScriptLoaded(result);
return NO;
}
return YES;
}
// Event emitter convenience method
- (const CustomWebViewEventEmitter &)eventEmitter
{
return static_cast<const CustomWebViewEventEmitter &>(*_eventEmitter);
}
+ (ComponentDescriptorProvider)componentDescriptorProvider
{
return concreteComponentDescriptorProvider<CustomWebViewComponentDescriptor>();
}
Class<RCTComponentViewProtocol> WebViewCls(void)
{
return RCTWebView.class;
}
@end
This code is written in Objective-C++ and contains various details:
- the
@interface
implements two protocols:RCTCustomWebViewViewProtocol
, generated by Codegen;WKNavigationDelegate
, provided by the WebKit frameworks to handle the web view navigation events;
- the
init
method that instantiate theWKWebView
, adds it to the subviews and that set thenavigationDelegate
; - the
updateProps
method that is called by React Native when the component's props change; - the
layoutSubviews
method that describe how the custom view needs to be laid out; - the
webView:didFinishNavigation:
method that let you handle the what do when theWKWebView
finishes loading the page; - the
urlIsValid:(std::string)propString
method that checks whether the URL received as prop is valid; - the
eventEmitter
method which is a utility to retrieve a strongly typedeventEmitter
instance - the
componentDescriptorProvider
which returns theComponentDescriptor
generated by Codegen; - the
WebViewCls
which is a helper method to register theRCTWebView
in the application.
AppDelegate.mm
Finally, you can register the component in the app.
Update the AppDelegate.mm
to make your application aware of our custom WebView component:
#import "AppDelegate.h"
#import <React/RCTBundleURLProvider.h>
#import <React/RCTBridge+Private.h>
#import "RCTWebView.h"
@implementation AppDelegate
// ...
- (NSDictionary<NSString *,Class<RCTComponentViewProtocol>> *)thirdPartyFabricComponents
{
NSMutableDictionary * dictionary = [super thirdPartyFabricComponents].mutableCopy;
dictionary[@"CustomWebView"] = [RCTWebView class];
return dictionary;
}
@end
This code override the thirdPartyFabricComponents
method by obtaining a mutable copy of the dictionary of third party's components coming from other sources, like third party libraries.
It then adds an entry to the dictionary with the name used in the Codegen specification file. In this way, when React requires to load a component with name CustomWebView
, React Native will instantiate a RCTWebView
.
Finally, it returns the new dictionary.
Add WebKit framework
This step is only required because we are creating a Web view. Web components on iOS needs to be linked against the WebKit framework provided by Apple. If your component doesn't need to access web-specific features, you can skip this step.
A web view requires access to some features that Apple provides through one of the frameworks shipped with Xcode and the devices: WebKit.
You can see it in the native code by the #import <WebKit/WebKit.h>
line added in the RCTWebView.mm
.
To link the WebKit framework in your app, follow these steps:
- In Xcode, Click on your project
- Select the app target
- Select the General tab
- Scroll down until you find the "Frameworks, Libraries, and Embedded Contents" section, and press the
+
button

- In the search bar, filter for WebKit
- Select the WebKit framework
- Click on Add.
