Skip to content

Commit c00e086

Browse files
authored
Merge pull request #362 from PSPDFKit/rad/add-instant-native-catalog-exampe
Bridge Instant Example from PSPDFKit Catalog to the NativeCatalog React Native project
2 parents c8e9142 + d9a0936 commit c00e086

26 files changed

+2039
-49
lines changed

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-native-pspdfkit",
3-
"version": "1.28.6",
3+
"version": "1.28.7",
44
"description": "A React Native module for the PSPDFKit library.",
55
"keywords": [
66
"react native",

samples/Catalog/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "Catalog",
3-
"version": "1.28.6",
3+
"version": "1.28.7",
44
"private": true,
55
"scripts": {
66
"start": "react-native start",

samples/NativeCatalog/App.js

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@ const examples = [
2929
name: 'Manual Signing',
3030
description:
3131
'Shows how to start the signing flow using a react-native button linked to our CustomPdfView.',
32-
action: component => {
32+
action: (component) => {
3333
if (Platform.OS === 'android') {
34-
requestExternalStoragePermission(function() {
35-
extractFromAssetsIfMissing('Form_example.pdf', function() {
34+
requestExternalStoragePermission(function () {
35+
extractFromAssetsIfMissing('Form_example.pdf', function () {
3636
component.props.navigation.navigate('ManualSigning');
3737
});
3838
});
@@ -45,10 +45,10 @@ const examples = [
4545
name: 'Watermark',
4646
description:
4747
'Shows how to watermark a PDF that is loaded in our CustomPdfView',
48-
action: component => {
48+
action: (component) => {
4949
if (Platform.OS === 'android') {
50-
requestExternalStoragePermission(function() {
51-
extractFromAssetsIfMissing('Form_example.pdf', function() {
50+
requestExternalStoragePermission(function () {
51+
extractFromAssetsIfMissing('Form_example.pdf', function () {
5252
component.props.navigation.navigate('Watermark');
5353
});
5454
});
@@ -61,10 +61,10 @@ const examples = [
6161
name: 'Watermark on Startup',
6262
description:
6363
'Shows how to watermark a PDF as soon as it is loaded in our CustomPdfView',
64-
action: component => {
64+
action: (component) => {
6565
if (Platform.OS === 'android') {
66-
requestExternalStoragePermission(function() {
67-
extractFromAssetsIfMissing('Form_example.pdf', function() {
66+
requestExternalStoragePermission(function () {
67+
extractFromAssetsIfMissing('Form_example.pdf', function () {
6868
component.props.navigation.navigate('WatermarkStartup');
6969
});
7070
});
@@ -77,10 +77,10 @@ const examples = [
7777
name: 'Default Annotation Settings',
7878
description:
7979
'Shows how to configure default annotations settings. (Android Only)',
80-
action: component => {
80+
action: (component) => {
8181
if (Platform.OS === 'android') {
82-
requestExternalStoragePermission(function() {
83-
extractFromAssetsIfMissing('Form_example.pdf', function() {
82+
requestExternalStoragePermission(function () {
83+
extractFromAssetsIfMissing('Form_example.pdf', function () {
8484
component.props.navigation.navigate('DefaultAnnotationSettings');
8585
});
8686
});
@@ -89,6 +89,15 @@ const examples = [
8989
}
9090
},
9191
},
92+
{
93+
name: 'Instant Example',
94+
description: 'Shows the Native Swift Instant Example. (iOS only)',
95+
action: (component) => {
96+
if (Platform.OS === 'ios') {
97+
component.props.navigation.push('InstantExample');
98+
}
99+
},
100+
},
92101
];
93102

94103
class ManualSigningScreen extends Component<{}> {
@@ -106,7 +115,7 @@ class ManualSigningScreen extends Component<{}> {
106115
ref="pdfView"
107116
document={this.state.documentPath}
108117
style={{flex: 1}}
109-
onDocumentDigitallySigned={event => {
118+
onDocumentDigitallySigned={(event) => {
110119
this.setState({
111120
documentPath: event.nativeEvent.signedDocumentPath,
112121
});
@@ -157,7 +166,7 @@ class WatermarkScreen extends Component<{}> {
157166
ref="pdfView"
158167
document={this.state.documentPath}
159168
style={{flex: 1}}
160-
onDocumentWatermarked={event => {
169+
onDocumentWatermarked={(event) => {
161170
this.setState({
162171
documentPath: event.nativeEvent.watermarkedDocumentPath,
163172
});
@@ -209,7 +218,7 @@ class WatermarkStartupScreen extends Component<{}> {
209218
ref="pdfView"
210219
document={this.state.documentPath}
211220
style={{flex: 1}}
212-
onDocumentWatermarked={event => {
221+
onDocumentWatermarked={(event) => {
213222
this.setState({
214223
documentPath: event.nativeEvent.watermarkedDocumentPath,
215224
});
@@ -220,6 +229,29 @@ class WatermarkStartupScreen extends Component<{}> {
220229
}
221230
}
222231

232+
class InstantExampleScreen extends Component<{}> {
233+
render() {
234+
return (
235+
<View style={{flex: 1}}>
236+
{Platform.OS === 'ios' && <CustomPdfView ref="pdfView" />}
237+
{Platform.OS === 'ios' && (
238+
<Button
239+
onPress={() => {
240+
NativeModules.CustomPdfViewManager.presentInstantExample(
241+
findNodeHandle(this.refs.pdfView),
242+
);
243+
}}
244+
title="Present Instant Example"
245+
/>
246+
)}
247+
{Platform.OS === 'android' && (
248+
<Text>This example isn't supported on Android!</Text>
249+
)}
250+
</View>
251+
);
252+
}
253+
}
254+
223255
class DefaultAnnotationSettingsScreen extends Component<{}> {
224256
render() {
225257
return (
@@ -259,6 +291,9 @@ export default createAppContainer(
259291
WatermarkStartup: {
260292
screen: WatermarkStartupScreen,
261293
},
294+
InstantExample: {
295+
screen: InstantExampleScreen,
296+
},
262297
DefaultAnnotationSettings: {
263298
screen: DefaultAnnotationSettingsScreen,
264299
},

samples/NativeCatalog/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ target 'YourApp' do
6161
pod 'react-native-pspdfkit', :path => '../node_modules/react-native-pspdfkit'
6262
- pod 'PSPDFKit', podspec: 'https://customers.pspdfkit.com/cocoapods/YOUR_COCOAPODS_KEY_GOES_HERE/pspdfkit/latest.podspec'
6363
+ pod 'PSPDFKit', podspec: 'https://customers.pspdfkit.com/cocoapods/USE_YOUR_OWN_COCOAPODS_KEY/pspdfkit/latest.podspec'
64+
- pod 'Instant', podspec:'https://customers.pspdfkit.com/cocoapods/YOUR_COCOAPODS_KEY_GOES_HERE/instant/latest.podspec'
65+
+ pod 'Instant', podspec:'https://customers.pspdfkit.com/cocoapods/USE_YOUR_OWN_COCOAPODS_KEY/instant/latest.podspec'
6466

6567
use_native_modules!
6668
end
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//
2+
// InstantDocumentInfo.swift
3+
// PSPDFKit
4+
//
5+
// Copyright © 2017-2020 PSPDFKit GmbH. All rights reserved.
6+
//
7+
// THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
8+
// AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT.
9+
// UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
10+
// This notice may not be removed from this file.
11+
//
12+
13+
/// The response from the PSPDFKit for Web examples server API that may be used to load a document layer with Instant.
14+
struct InstantDocumentInfo {
15+
let serverURL: URL
16+
let url: URL
17+
let jwt: String
18+
}
19+
20+
extension InstantDocumentInfo: Equatable {
21+
public static func == (lhs: InstantDocumentInfo, rhs: InstantDocumentInfo) -> Bool {
22+
return lhs.serverURL == rhs.serverURL && lhs.url == rhs.url && lhs.jwt == rhs.jwt
23+
}
24+
}
25+
26+
extension InstantDocumentInfo {
27+
enum JSONKeys: String {
28+
case serverUrl, url, documentId, jwt
29+
}
30+
31+
init?(json: Any) {
32+
guard let dictionary = json as? [String: Any],
33+
let serverUrlString = dictionary[JSONKeys.serverUrl.rawValue] as? String,
34+
let serverURL = URL(string: serverUrlString),
35+
let urlString = dictionary[JSONKeys.url.rawValue] as? String,
36+
let url = URL(string: urlString),
37+
let jwt = dictionary[JSONKeys.jwt.rawValue] as? String else {
38+
return nil
39+
}
40+
41+
self.init(serverURL: serverURL, url: url, jwt: jwt)
42+
}
43+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
//
2+
// Copyright © 2018-2020 PSPDFKit GmbH. All rights reserved.
3+
//
4+
// THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
5+
// AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT.
6+
// UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
7+
// This notice may not be removed from this file.
8+
//
9+
10+
import PSPDFKitUI
11+
12+
/**
13+
Connects to our public PSPDFKit for Web examples server to present documents managed by PSPDFKit Instant.
14+
This is decoupled from any view controller in order to support loading documents by opening URLs.
15+
*/
16+
public class InstantDocumentPresenter {
17+
/**
18+
Interfaces with our Web examples server to create and access documents.
19+
In your own app you would connect to your own server backend to get Instant document identifiers and authentication tokens.
20+
*/
21+
private lazy var apiClient = WebExamplesAPIClient(presentingViewController: presentingViewController)
22+
23+
/// View controller that can present other view controllers.
24+
private weak var presentingViewController: UIViewController?
25+
26+
/// Initialize a document presenter with a view controller that can present other view controllers.
27+
public init(presentingViewController: UIViewController) {
28+
self.presentingViewController = presentingViewController
29+
}
30+
31+
/// Tries to create a new Instant session.
32+
public func createNewSession() {
33+
loadSession(loadingMessage: "Creating") { completion in
34+
self.apiClient.createNewSession(completion: completion)
35+
}
36+
}
37+
38+
/**
39+
Tries to join an existing Instant session or shows an alert on failure.
40+
41+
- Parameter urlString: An Instant document URL scanned from a barcode or entered by the user.
42+
*/
43+
public func joinExistingSession(withURL urlString: String) {
44+
guard let url = URL(string: urlString) else {
45+
showAlert(withTitle: "Couldn’t Join Group", message: "This is not a link. Please enter an Instant document link.")
46+
return
47+
}
48+
49+
loadSession(loadingMessage: "Verifying") { completion in
50+
self.apiClient.resolveExistingSessionURL(url, completion: completion)
51+
}
52+
}
53+
54+
/**
55+
Loads an Instant session using the specified API call. The passed in closure should run an
56+
asynchronous API call. That closure will be passed another closure to run on completion.
57+
*/
58+
private func loadSession(loadingMessage: String, APICall: @escaping (@escaping WebExamplesAPIClient.CompletionHandler) -> Void) {
59+
let progressHUDItem = StatusHUDItem.indeterminateProgress(withText: loadingMessage)
60+
progressHUDItem.setHUDStyle(.black)
61+
62+
progressHUDItem.push(animated: true, on: presentingViewController?.view.window) {
63+
APICall { result in
64+
DispatchQueue.main.async {
65+
progressHUDItem.pop(animated: true, completion: nil)
66+
67+
switch result {
68+
case let .success(documentInfo):
69+
do {
70+
try self.openDocument(with: documentInfo)
71+
} catch {
72+
self.showAlert(withTitle: "Couldn’t Set Up Instant", message: error.localizedDescription)
73+
}
74+
75+
case .failure(WebExamplesAPIClient.Failure.cancelled):
76+
break // Do not show the alert if user cancelled the request themselves.
77+
78+
case let .failure(otherError):
79+
self.showAlert(withTitle: "Couldn’t Get Instant Document Info", message: otherError.localizedDescription)
80+
}
81+
}
82+
}
83+
}
84+
}
85+
86+
private func openDocument(with info: InstantDocumentInfo) throws {
87+
let instantViewController = try InstantDocumentViewController(documentInfo: info)
88+
89+
let navigationController = UINavigationController(rootViewController: instantViewController)
90+
navigationController.modalPresentationStyle = .fullScreen
91+
92+
presentOnFrontmostViewController(navigationController)
93+
}
94+
95+
private func showAlert(withTitle title: String, message: String) {
96+
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
97+
alertController.addAction(UIAlertAction(title: "OK", style: .cancel))
98+
99+
presentOnFrontmostViewController(alertController)
100+
}
101+
102+
private func presentOnFrontmostViewController(_ viewController: UIViewController) {
103+
guard let rootViewController = presentingViewController?.viewIfLoaded?.window?.rootViewController else {
104+
print("Couldn’t show view controller because no root view controller was found.")
105+
return
106+
}
107+
108+
rootViewController.psc_frontmost.present(viewController, animated: true, completion: nil)
109+
}
110+
}

0 commit comments

Comments
 (0)