I'll start this blog post by saying that I have gained tremendous respect for my colleagues who use React Native day in and day out. The sheer number of tools needed to run and compile a single React Native project is truly overwhelming.
Introduction
The Promise
React Native promises to significantly reduce the workload needed to produce a multi-platform app. With just a single JavaScript project, an individual can create both an iOS and Android app that offer consistent behavior and appearance.
The Reality
When tasked with building custom experiences, like the one my client required, you quickly realize that three integrated development environments (IDEs) must run side-by-side to get the app functioning.
Managing attention across three distinct contexts—iOS, Web, and Android—is already a challenge. But then, you're hit with platform-specific errors. These errors are not only obscure but are further complicated by the lack of robust debugging tools for React Native.
The Problem
We're attempting to construct what's termed as a React Native Native Component iOS. This leverages a native Swift XCFramework named Wowza Flowplayer Apple SDK, which I developed.
The Wowza Flowplayer Apple SDK is a fully integrated video player that supports various media types, including VODs and livestreams. Despite its compact size and customizability, it operates seamlessly, demanding minimal device resources thanks to its efficient architecture.
Step 1: Setting Up the Project
Starting a project using the react-native-cli and manually linking the Native Module can quickly become chaotic due to the extensive linking required.
Instead, I recommend utilizing the react-native-builder-bob CLI from Callstack. This tool is incredibly user-friendly:
npx create-react-native-library@latest my-library
Answer the questions of the CLI:
? What is the name of the npm package? › <NAME>
? What is the description for the package? › <DESCRIPTION>
? What is the name of package author? › <DEVELOPER>
? What is the email address for the package author? › <EMAIL>
? What is the URL for the package author? › <URL>
? What is the URL for the repository? › <GIT_URL>
? What type of library do you want to develop? › Native view
? Which languages do you want to use? › Kotlin & Swift
All that remains is for us to cd into our project root and install the dependencies:
cd my-library
yarn
Step 2: Exploring the Code
JavaScript
Upon opening the src directory, you'll encounter the TypeScript code:
import {
requireNativeComponent,
UIManager,
Platform,
type ViewStyle,
} from "react-native";
type AwesomeLibraryProps = {
color: string,
style: ViewStyle,
};
const ComponentName = "AwesomeLibraryView";
export const AwesomeLibraryView =
UIManager.getViewManagerConfig(ComponentName) != null
? requireNativeComponent<AwesomeLibraryProps>(ComponentName)
: () => {
throw new Error(LINKING_ERROR);
};
iOS
Let's look at the iOS side. The Bridging-Header file acts as an Objective-C header, creating a bridge between our Swift code and Objective-C:
#import <React/RCTViewManager.h>
The ViewManager.m file exports our view manager:
#import <React/RCTViewManager.h>
@interface RCT_EXTERN_MODULE(AwesomeLibraryViewManager, RCTViewManager)
RCT_EXPORT_VIEW_PROPERTY(color, NSString)
@end
And the actual Swift implementation:
@objc(AwesomeLibraryViewManager)
class AwesomeLibraryViewManager: RCTViewManager {
override func view() -> (AwesomeLibraryView) {
return AwesomeLibraryView()
}
@objc override static func requiresMainQueueSetup() -> Bool {
return false
}
}
class AwesomeLibraryView : UIView {
@objc var color: String = "" {
didSet {
self.backgroundColor = hexStringToUIColor(hexColor: color)
}
}
}
Step 3: Incorporating 3rd Party Swift Dependencies
We'll leverage CocoaPods. Open the .podspec file and add the dependency:
# Set FlowplayerSDK dependency
s.dependency "FlowplayerSDK", '~> 4.1.0'
s.dependency "GoogleAds-IMA-iOS-SDK", '~> 3.19.1'
Then run:
yarn clean
yarn
If you need to support iOS 14+, update the Podfile:
min_ios_versions_supported = ['14.0', min_ios_version_supported]
index_of_max = min_ios_versions_supported.each_with_index.max_by { |number, _| number.to_f }[1]
platform :ios, min_ios_versions_supported[index_of_max]
prepare_react_native_project!
Step 4: Utilizing our 3rd Party Dependency
Now we can import and use our dependency:
import FlowplayerSDK
// Add the code for setup here
Note: To get intellisense when developing React Native iOS Modules, open
example/ios/AwesomeLibraryExample.xcworkspace→Pods→Development Pods→react-native-awesome-library. Edit all your files from here as the Example workspace has the context of all libraries and frameworks.
Conclusion
It's been quite the journey, plagued with platform-specific intricacies, just to get our dependency to load in a manner that's update-friendly via the CLI and CocoaPods. It's my sincerest wish that this blog post spares any other developer the same strenuous path I've had to traverse.
GitHub repo for reference: github.com/br3akzero
Cheers to perseverance and the love of code,
Break Zero.