christopherdro / react-native-print

Print documents using React Native
MIT License
335 stars 142 forks source link

working around rdar://FB11966380 (iOS 16 blank page issue with label printers) #201

Closed punkstar closed 11 months ago

punkstar commented 1 year ago

Radar Issue: https://openradar.appspot.com/radar?id=5548908621070336

There's a bug in iOS 16 right now that causes an issue when printing anything but the first label / page of a document being printed.

I was originally using expo-print to implement PDF label printing in our app. I moved over to this library to try and skirt around the issue but ran into the same problem.

I've managed to work around this issue by implementing the logic suggested by @alexbeckwith https://github.com/expo/expo/issues/19399#issuecomment-1450982304 and the example from @JacopoOrlandini https://github.com/expo/expo/issues/19399#issuecomment-1456537548 *(tagging you to say thanks!), caching the printer to avoid needing to look up paper sizes again.

I've attached the patch that I'm running to fix this issue if anyone needs to work around this themselves.

I won't open a pull request because I've not got enough experience of the ecosystem to know if this is a good idea or not, but if anyone else reading this does, then I hope this gives you a head start.

diff --git a/src/lib/vendor/christopherdro/react-native-print/ios/RNPrint/RNPrint.h b/src/lib/vendor/christopherdro/react-native-print/ios/RNPrint/RNPrint.h
index 081bf6b..11c5485 100644
--- a/src/lib/vendor/christopherdro/react-native-print/ios/RNPrint/RNPrint.h
+++ b/src/lib/vendor/christopherdro/react-native-print/ios/RNPrint/RNPrint.h
@@ -5,11 +5,15 @@
 #import <React/RCTView.h>
 #import <React/RCTBridgeModule.h>

-@interface RNPrint : RCTView <RCTBridgeModule, UIPrintInteractionControllerDelegate, UIPrinterPickerControllerDelegate>
+@interface RNPrint : RCTView <RCTBridgeModule, UIPrintInteractionControllerDelegate, UIPrinterPickerControllerDelegate> {
+    NSMutableDictionary<NSURL*,UIPrinter*> *_lastPrinterUsed;
+}
+
 @property UIPrinter *pickedPrinter;
 @property NSString *filePath;
 @property NSString *htmlString;
 @property NSString *jobName;
 @property NSURL *printerURL;
 @property (nonatomic, assign) BOOL isLandscape;
+@property (nonatomic,retain) NSMutableDictionary<NSURL*,UIPrinter*> *lastPrinterUsed;
 @end
diff --git a/src/lib/vendor/christopherdro/react-native-print/ios/RNPrint/RNPrint.m b/src/lib/vendor/christopherdro/react-native-print/ios/RNPrint/RNPrint.m
index dcc4867..10c07dc 100644
--- a/src/lib/vendor/christopherdro/react-native-print/ios/RNPrint/RNPrint.m
+++ b/src/lib/vendor/christopherdro/react-native-print/ios/RNPrint/RNPrint.m
@@ -12,6 +12,8 @@
     return dispatch_get_main_queue();
 }

+@synthesize lastPrinterUsed = _lastPrinterUsed;
+
 RCT_EXPORT_MODULE();

 -(void)launchPrint:(NSData *) data
@@ -33,6 +35,7 @@ RCT_EXPORT_MODULE();
     printInfo.duplex = UIPrintInfoDuplexLongEdge;
     printInfo.orientation = _isLandscape? UIPrintInfoOrientationLandscape: UIPrintInfoOrientationPortrait;

+
     printInteractionController.printInfo = printInfo;
     printInteractionController.showsPageRange = YES;

@@ -47,7 +50,7 @@ RCT_EXPORT_MODULE();
     void (^completionHandler)(UIPrintInteractionController *, BOOL, NSError *) =
     ^(UIPrintInteractionController *printController, BOOL completed, NSError *error) {
         if (!completed && error) {
-            NSLog(@"Printing could not complete because of error: %@", error);
+            NSLog(@"[react-native-print] Printing could not complete because of error: %@", error);
             reject(RCTErrorUnspecified, nil, RCTErrorWithMessage(error.description));
         } else {
             resolve(completed ? printInfo.jobName : nil);
@@ -88,7 +91,19 @@ RCT_EXPORT_METHOD(print:(NSDictionary *)options

     if (options[@"printerURL"]){
         _printerURL = [NSURL URLWithString:[RCTConvert NSString:options[@"printerURL"]]];
-        _pickedPrinter = [UIPrinter printerWithURL:_printerURL];
+
+        if (self.lastPrinterUsed == nil) {
+            self.lastPrinterUsed = [[NSMutableDictionary alloc] init];
+        }
+
+        if (! [self.lastPrinterUsed objectForKey:_printerURL]) {
+            NSLog(@"[react-native-print] Printer not found in cache, adding it");
+            _pickedPrinter = [UIPrinter printerWithURL:_printerURL];
+            [self.lastPrinterUsed setObject:_pickedPrinter forKey:_printerURL];
+        } else {
+            NSLog(@"[react-native-print] Printer found in cache, using it");
+            _pickedPrinter = [self.lastPrinterUsed objectForKey:_printerURL];
+        }
     }

     if(options[@"isLandscape"]) {
@@ -136,7 +151,7 @@ RCT_EXPORT_METHOD(selectPrinter:(NSDictionary *)options
     void (^completionHandler)(UIPrinterPickerController *, BOOL, NSError *) =
     ^(UIPrinterPickerController *printerPicker, BOOL userDidSelect, NSError *error) {
         if (!userDidSelect && error) {
-            NSLog(@"Printing could not complete because of error: %@", error);
+            NSLog(@"[react-native-print] Printing could not complete because of error: %@", error);
             reject(RCTErrorUnspecified, nil, RCTErrorWithMessage(error.description));
         } else {
             [UIPrinterPickerController printerPickerControllerWithInitiallySelectedPrinter:printerPicker.selectedPrinter];

I'll let stale bot close this issue automatically.

stale[bot] commented 1 year ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.