Sitemap
John Luong

John Luong

Fabic Component

3 min readOct 19, 2025
  1. Run
npx create-react-native-library@latest react-native-fast-pdf-viewer

2. Run

cd react-native-fast-pdf-viewer && yarn install 
cd example && yarn install
cd ios && pod install && cd ..
yarn ios

3. At root-project/src/FastPdfViewerViewNativeComponent.ts

import { codegenNativeComponent, type ViewProps } from 'react-native';

interface NativeProps extends ViewProps {
color?: string;
}

export default codegenNativeComponent<NativeProps>('FastPdfViewerView');

3. At root-project/src/index.tsx

export { default as FastPdfViewerView } from './FastPdfViewerViewNativeComponent';
export * from './FastPdfViewerViewNativeComponent';

Fabic Component for IOS

4. In root Project at FastPdfViewer.podspec add swift in s.source_files to support for Swift File

Press enter or click to view image in full size

5. Open Xcode and select path root-project/example/ios

Get John Luong’s stories in your inbox

Join Medium for free to get updates from this writer.

6. Go to Pods/Development Pods/FastPdfViewer -> click right -> New File from Template -> Swift File (not SwiftUI) -> Save file in root-project/ios (not in example/ios) -> Put name FastPdfViewerViewUI -> create

if the message “want you create brigde-header” -> choose Dont’t Create

Press enter or click to view image in full size

7. FastPdfViewerViewUI.swift


import Foundation
import UIKit
import PDFKit

@objc public class FastPdfViewerViewUI: UIView {

private var pdfView: PDFView?

@objc public override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .lightGray
}

required init?(coder: NSCoder) {
super.init(coder: coder)
}

// MARK: - Load PDF from URI (local or remote)
@objc public func loadPdf(from uri: NSString?) {
guard let uri = uri as String? else { return }

// Remove old pdfView if exists
pdfView?.removeFromSuperview()
pdfView = PDFView(frame: bounds)
guard let pdfView = pdfView else { return }

pdfView.autoScales = true
pdfView.backgroundColor = .white
addSubview(pdfView)
pdfView.translatesAutoresizingMaskIntoConstraints = false

NSLayoutConstraint.activate([
pdfView.leadingAnchor.constraint(equalTo: leadingAnchor),
pdfView.trailingAnchor.constraint(equalTo: trailingAnchor),
pdfView.topAnchor.constraint(equalTo: topAnchor),
pdfView.bottomAnchor.constraint(equalTo: bottomAnchor)
])

if uri.lowercased().hasPrefix("http") {
// Remote PDF
loadRemotePdf(urlString: uri, into: pdfView)
} else {
// Local PDF
let fileURL = URL(fileURLWithPath: uri)
pdfView.document = PDFDocument(url: fileURL)
}
}

private func loadRemotePdf(urlString: String, into pdfView: PDFView) {
guard let url = URL(string: urlString) else { return }
let session = URLSession.shared
let task = session.dataTask(with: url) { [weak self] data, _, error in
guard let self = self, let data = data, error == nil else { return }
DispatchQueue.main.async {
if let document = PDFDocument(data: data) {
pdfView.document = document
} else {
self.showErrorLabel("Failed to open PDF")
}
}
}
task.resume()
}

private func showErrorLabel(_ text: String) {
let label = UILabel()
label.text = text
label.textColor = .red
label.textAlignment = .center
label.frame = bounds
addSubview(label)
}

// MARK: - Background Color
@objc public func updateBackgroundColor(_ hex: NSString?) {
guard let hex = hex as String? else { return }
backgroundColor = UIColor(hex: hex)
}
}

private extension UIColor {
convenience init?(hex: String) {
var cString = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased()
if cString.hasPrefix("#") { cString.removeFirst() }
guard cString.count == 6 else { return nil }
var rgbValue: UInt64 = 0
Scanner(string: cString).scanHexInt64(&rgbValue)
self.init(
red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0,
green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0,
blue: CGFloat(rgbValue & 0x0000FF) / 255.0,
alpha: 1.0
)
}
}

8. FastPdfViewerView.mm

#import "FastPdfViewerView.h"

#import <react/renderer/components/FastPdfViewerViewSpec/ComponentDescriptors.h>
#import <react/renderer/components/FastPdfViewerViewSpec/EventEmitters.h>
#import <react/renderer/components/FastPdfViewerViewSpec/Props.h>
#import <react/renderer/components/FastPdfViewerViewSpec/RCTComponentViewHelpers.h>

#import "RCTFabricComponentsPlugins.h"
#import "FastPdfViewer-Swift.h"

using namespace facebook::react;

@interface FastPdfViewerView () <RCTFastPdfViewerViewViewProtocol>
@end

@implementation FastPdfViewerView {
FastPdfViewerViewUI *_pdfView; // ✅ Swift UIView
}

+ (ComponentDescriptorProvider)componentDescriptorProvider {
return concreteComponentDescriptorProvider<FastPdfViewerViewComponentDescriptor>();
}

- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
static const auto defaultProps = std::make_shared<const FastPdfViewerViewProps>();
_props = defaultProps;

// ✅ Dùng UIView Swift thay vì UIView thuần
_pdfView = [FastPdfViewerViewUI new];
self.contentView = _pdfView;
}
return self;
}


- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps {
const auto &oldViewProps = *std::static_pointer_cast<FastPdfViewerViewProps const>(_props);
const auto &newViewProps = *std::static_pointer_cast<FastPdfViewerViewProps const>(props);


// ✅ Cập nhật PDF URI
if (oldViewProps.uri != newViewProps.uri) {
NSString *uriString = [[NSString alloc] initWithUTF8String:newViewProps.uri.c_str()];
[_pdfView loadPdfFrom:uriString];
}

[super updateProps:props oldProps:oldProps];
}

Class<RCTComponentViewProtocol> FastPdfViewerViewCls(void) {
return FastPdfViewerView.class;
}

@end

9. Run

cd example/ios && pod install
yarn install

--

--