UIPrintInteractionController.shared on Mac OS Sonoma running iPad App

After updating to Mac OS Sonoma, we have encountered compatibility issues with our iPad-designed application, specifically with the AirPrint functionality, when it is run on MacOS. The AirPrint feature stopped working properly through UIPrintInteractionController.shared.

We have noticed that when we compile the application using Catalyst, the AirPrint functionality is restored and works as expected. However, this solution is not viable for us due to the restrictions associated with the frameworks we are utilizing.

We are seeking alternative solutions, and any help or guidance would be highly appreciated to resolve this issue and ensure a seamless and uninterrupted user experience in our application.

STEPS TO REPRODUCE

  1. Create an app for ipad with just a button and this code
var str = "TEST"
let printInfo = UIPrintInfo(dictionary:nil)
printInfo.outputType = .general
printInfo.jobName = "Report"
printInfo.orientation = .portrait

let printController = UIPrintInteractionController.shared
printController.printInfo = printInfo
printController.showsNumberOfCopies = false
printController.showsPageRange = false
printController.showsNumberOfCopies = false

let formatter = UIMarkupTextPrintFormatter(markupText: str)
formatter.contentInsets = UIEdgeInsets(top: 72, left: 72, bottom: 72, right: 72)
printController.printFormatter = formatter
printController.present(animated: true, completionHandler: nil)

2.Run it on a MacOS with Sonoma, there is no error on console or anything but it don't work.

-If you run it with Catalyst it just works when adding the Printing permission of App Sandbox in Signing & Capabilities.

Does this occur when attempting to print? or presenting the print option?

This sounds similar to the issue being discussed here: https://stackoverflow.com/questions/77555891/printing-issue-running-a-designed-for-ipad-app-on-macos/77570505

I have been testing exclusively on macOS 14+ and, unfortunately, I don't have a machine running an earlier macOS version on which to test.

Did you file a feedback report with Apple?

I would encourage anyone encountering this problem to file their own feedback report and reference FB13420491.

Has anyone found a solution? Printing no longer works in all of our apps on Sonoma 14.3.1 as "Designed tfor iPad" app. The detour via a WKWebView or PDF is not a solution for us because we print images in the exact size.

The new macOS version Sonoma 14.4 has just been released. Unfortunately the bug is still not fixed. It is still not possible to print with a "Designed for iPad" app. However, more errors are now displayed in the console:

CLIENT ERROR: TUINSRemoteViewController does not override -viewServiceDidTerminateWithError: and thus cannot react to catastrophic errors beyond logging them
Failed to connect (genericPrinterImage) outlet from (PMPrinterSelectionController) to (NSImageView): missing setter or instance variable
Failed to connect (localPrintersLabel) outlet from (PMPrinterSelectionController) to (NSTextField): missing setter or instance variable
Failed to connect (otherPrintersLabel) outlet from (PMPrinterSelectionController) to (NSTextField): missing setter or instance variable
Failed to connect (recentPrintersLabel) outlet from (PMPrinterSelectionController) to (NSTextField): missing setter or instance variable
CGContextClipToRect: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.

We have now noticed an interesting behavior in one of our apps. The app uses the same code to print from two different UIViewControllers. On a Mac as an iPad app it works without any problems in one of the two ViewControllers. The print preview appears and you can print. But in the other ViewController it doesn't work. The print preview does not appear.

Unfortunately we cannot find the difference that causes the different behavior. The ViewController in which it works is called with push segue from the first controller. We thought that maybe that was the important difference. But in another app of ours we have exactly the same configuration and printing in the second ViewController doesn't work in this app.

We have finally found a solution for this problem. Where it works, we load the UIImage from a JPG file. Where it doesn't work, the UIImage is created programmatically. The solution is to convert the UIImage to JPEG data and then create a new UIImage for printing. With this new UIImage, printing finally also works on a Mac in an iPad app.

UIImage *imageToPrint = [UIImage imageWithData:UIImageJPEGRepresentation(originalImage, 1)];

And an html string can be printed using a UIGraphicsBeginPDFContextToData. The disadvantage is that you have to define a fixed page size, but it prints with this solution in an iPad app on the Mac.

- (NSData *)createPDFfromHTML:(NSString *)htmlString 
{
    UIMarkupTextPrintFormatter *formatter = [[UIMarkupTextPrintFormatter alloc] initWithMarkupText:htmlString];
    UIPrintPageRenderer *pageRenderer = [[UIPrintPageRenderer alloc] init];
    [pageRenderer addPrintFormatter:formatter startingAtPageAtIndex:0];

    NSMutableData *pdfData = [NSMutableData data];
    CGRect paperRect = CGRectMake(0, 0, 595.3, 841.9); // A4
    CGRect printableRect = CGRectInset(paperRect, 72, 72); // 1-inch margins

    UIGraphicsBeginPDFContextToData(pdfData, paperRect, nil);
    [pageRenderer setValue:[NSValue valueWithCGRect:paperRect] forKey:@"paperRect"];
    [pageRenderer setValue:[NSValue valueWithCGRect:printableRect] forKey:@"printableRect"];
   
    NSInteger pages = [pageRenderer numberOfPages];
    
    for (NSInteger i = 0; i < pages; i++) {
        UIGraphicsBeginPDFPageWithInfo(paperRect, nil);
        [pageRenderer drawPageAtIndex:i inRect:paperRect];
    }

    UIGraphicsEndPDFContext();

    return pdfData;
}

- (void)printHtmlTest
{
    NSString *htmlContent = @"<h1>Hello World</h1>";
   
    NSData * pdfData = [self createPDFfromHTML:htmlContent];
    
    UIPrintInteractionController *printController = [UIPrintInteractionController sharedPrintController];
    
    printController.printingItem = pdfData;
    
    UIViewController *controller = [[UIViewController alloc] init];
    
    [printController presentAnimated:YES completionHandler:^(UIPrintInteractionController *printInteractionController, BOOL completed, NSError *error)
    {
        //
    }];
}

It turns out that our solution is not really a solution. It works with a single image, but not always. If we print multiple images on one page or on multiple pages, it doesn't work on the Mac. It doesn't matter whether we convert the images to JPG beforehand or not. It seems to be a timing problem. If we don't do anything in drawPageAtIndex, the print preview always appears. If we do a lot in drawPageAtIndex, the print preview doesn't appear on the Mac. It worked in earlier macOS versions. It always works on the iPhone and iPad. Unfortunately, we haven't been able to find a solution. It would be ideal if we had the option to only draw something in drawPageAtIndex when the print preview is already displayed.

We have the instance method printInteractionControllerDidPresentPrinterOptions. This is called a moment before the print preview has actually been displayed. But we could set a flag from this method with a delay of perhaps one second so that something should only be drawn in drawPageAtIndex from then on. However, we are missing the option to restart the drawing in the print preview using the programming code. If, for example, the print orientation is changed manually in the print preview dialog, then drawPageAtIndex is called again and everything is drawn correctly.

Does anyone know a way to restart the drawing in drawPageAtIndex of the UIPrintPageRenderer from the programming code?

FB13420491 is claimed to be fixed in macOS 15 WWDC developer beta 1 (24A5264n).

In my testing, things seem to work OK now on this macOS build. It would be good to hear what others are seeing for their use cases.

UIPrintInteractionController.shared on Mac OS Sonoma running iPad App
 
 
Q