我是一名 React Native 开发者,正在学习 Swift。我为我的应用程序开发了一个NativeModule,它可以比较两个 PDF 并总结更改结果,然后将其发送回 React Native Bridge。以下是我正在使用的代码,如果 PDF 包含的页面少于 100 页,这些代码可以工作,但是,当页面增加时,应用程序会因内存问题而崩溃。
本机模块(PDF 管理器):
import Foundation
import PDFKit
import SwiftDiff
@objc(PDFManager)
class PDFManager: NSObject {
private var currentPageIndex: Int = 0
private var lastProcessedIndex: Int = 0
@objc
func compareAndMerge(_ pdfPath1: String, pdfPath2: String, documentName: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
do {
guard let firstPdfURL = URL(string: pdfPath1),
let secondPdfURL = URL(string: pdfPath2) else {
reject("pdfInvalidPath", "Invalid PDF path", nil)
return
}
guard let pdf1 = PDFDocument(url: firstPdfURL),
let pdf2 = PDFDocument(url: secondPdfURL) else {
reject("PDFLoadError", "Failed to load PDFs", nil)
return
}
let summary = try compareAndSummarize(doc1: pdf1, doc2: pdf2)
let json = summarizeToJSON(summary: summary)
resolve(json)
} catch {
print("Error merging PDFs:", error)
reject("pdfMergeError", "Error merging PDFs", nil)
}
}
@objc
static func requiresMainQueueSetup() -> Bool {
return true
}
private func compareAndSummarize(doc1: PDFDocument, doc2: PDFDocument) throws -> (String, Int) {
var changesSummary = ""
var pagesChanged = 0
// Iterate over pages of doc1 and doc2 simultaneously
let pageCount = min(doc1.pageCount, doc2.pageCount)
for pageIndex in 0..<pageCount {
autoreleasepool {
guard let page1 = doc1.page(at: pageIndex),
let page2 = doc2.page(at: pageIndex) else { return }
let page1Text = page1.string ?? ""
let page2Text = page2.string ?? ""
changesSummary += "Page \(pageIndex + 1):\n"
// Calculate differences between the text of the two pages
let diff = diff(text1: page1Text, text2: page2Text)
var pageChanged = false
// Process each change in the differences
for change in diff {
switch change {
case .delete(let value):
pageChanged = true
changesSummary += "- Deleted: \(value)\n"
case .insert(let value):
pageChanged = true
changesSummary += "+ Inserted: \(value)\n"
case .equal( _):
break
}
}
if pageChanged {
pagesChanged += 1
}
}
}
return (changesSummary, pagesChanged)
}
func summarizeToJSON(summary: (String, Int)) -> String {
let dictionary: [String: Any] = ["changes": summary.0, "pagesChanged": summary.0]
let data = try? JSONSerialization.data(withJSONObject: dictionary, options: [.prettyPrinted])
return String(data: data!, encoding: .utf8) ?? ""
}
}
React 本机代码:
import React, {useState} from 'react';
import {ActivityIndicator, NativeModules, View, useWindowDimensions} from 'react-native';
import Text from '../../Components/Text';
import s from '../../assets/styles';
import {Button} from '../../Components/Buttons';
import DocumentPicker from 'react-native-document-picker';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
function HomeScreen() {
const [pdf1, setPDF1] = useState(null);
const [pdf2, setPDF2] = useState(null);
const [merging, setMerging] = useState(false);
const {PDFManager} = NativeModules;
const insets = useSafeAreaInsets();
const {width} = useWindowDimensions();
const buttonWidth = width / 2 - 25;
const selectFirstPDF = async () => {
try {
const result = await DocumentPicker.pickSingle({
type: DocumentPicker.types.pdf,
copyTo: 'cachesDirectory',
});
if (!result?.fileCopyUri) {
return;
}
setPDF1(result);
} catch (error) {}
};
const selectSecondPDF = async () => {
try {
const result = await DocumentPicker.pickSingle({
type: DocumentPicker.types.pdf,
copyTo: 'cachesDirectory',
});
if (!result?.fileCopyUri) {
return;
}
setPDF2(result);
} catch (error) {}
};
const updateManuals = async () => {
setMerging(true);
try {
const result = await PDFManager.compareAndMerge(
pdf1?.fileCopyUri,
pdf2?.fileCopyUri,
`${pdf2?.name}_Autopilot3.pdf`,
);
setMerging(false);
if (result) {
const json = JSON.parse(result);
console.log('json -> ', json);
}
} catch (error) {
console.log('Error -> ', error);
} finally {
setMerging(false);
}
};
return (
<View style={[s.flex1, {paddingTop: insets.top}, s.jcac]}>
<View style={[s.mHor10]}>
<Text style={[s.f30, s.ls2, s.fontBlack, s.textCenter, s.as]}>
Keep Highlights
</Text>
<Text style={[s.fontSemiBold, s.mVer10]}>
Effortlessly compare your PDF manuals and keep your highlights
updated. Files are compared locally and your data always remain
confidential.
</Text>
<View style={[s.flexRow, s.aic, s.jsb, s.mVer10]}>
<Button
title={pdf1?.name ? pdf1?.name : 'Highlighted PDF'}
style={[s.bgPurple, s.br30, {width: buttonWidth}, s.me10]}
textStyle={[s.fontMedium, s.f14, s.textWhite]}
onPress={selectFirstPDF}
/>
<Button
title={pdf2?.name ? pdf2?.name : 'Updated manual'}
onPress={selectSecondPDF}
textStyle={[s.fontMedium, s.f14, s.textWhite]}
style={[s.br30, {width: buttonWidth}]}
/>
</View>
<Button
title={'Get updated manual'}
isDisabled={!pdf1?.fileCopyUri || !pdf2?.fileCopyUri}
onPress={updateManuals}
/>
{merging && <ActivityIndicator size={'large'} color={'white'}/>}
</View>
</View>
);
}
export default HomeScreen;
我尝试了不同的技巧和方法,例如:autorelease。
没错,你应该使用Linux,而不是iOS