David Gelphman's Blog


1 Comment

Adding Save PDF to iBooks Support to Your Application

As I wrote recently, the ability to create a PDF document from an application’s printed content is one of my favorite features that Apple added in iOS 9. This feature leverages the printing system so that if your application can print, a user will also be able to use the new “Save PDF to iBooks” feature. Well, almost.

The key is that your application needs to offer printing by using Apple’s “Share Sheet”, instead of using the older method of using a UIPrintInteractionController. And unfortunately, as of my writing this, all of Apple’s sample code demonstrating how to implement printing in an iOS application uses the UIPrintInteractionController method of presenting printing to the user.

I ran into this recently when I emailed the developers of one of my favorite iOS applications and suggested they add support for the “Save PDF to iBooks” to their app. After all, the app already supported the Share Sheet for a number of tasks other than printing but it supported printing using the old UIPrintInteractionController. The developer was kind enough to reply to my suggestion with interest, but the response I got was: “I’d like to but I don’t know how to do that.”

Poking around Apple’s developer site, I found the handy page AirPrint for Developers. It includes a link to the 2014 WWDC session on AirPrint (there was none in 2015) which is a great presentation with tons of info for app developers getting started with printing on iOS. And the AirPrint for Developers page has four links to sample code that implement printing in various ways. Unfortunately none of these samples implement the Share Sheet method of printing.

So I grabbed one of Apple’s samples and updated it. I started with one that I myself wrote (long ago) at Apple. It demonstrates how to print content from a WebView in both a basic way and also by using some fancier techniques to add more control to the layout of content on the printed pages. Updating it to use the Share Sheet is pretty straightforward.

The only substantive change needed to make the sample app use the share sheet is to take out the use of the UIPrintInteractionController from the sample and replaced it with the use of a UIActivityViewController. This controller takes an array of “Activity Items” as part of its initialization. When the controller is presented it determines what activities (or actions) can be taken on those activity items and allows the user to choose from those actions.

The UIActivityViewController determines that printing is a supported activity if the activity items array contains both an object of the UIPrintInfo class and also a source of print data. The source of print data can be a printing item or items (e.g. a photo or photos), a UIPrintFormatter object, or a UIPrintPageRenderer subclass. For the original sample code, the source of print data was a UIPrintPageRenderer subclass that the code created and assigned to the myRenderer local variable. For fun in the new code I also added an NSURL corresponding to the URL of the web page the user is visiting. This allows the Share Sheet to show more actions for the sample app than just those associated with printing. Once the UIActivityViewController is created, all that is needed is to present it and UIKit does the rest. I mostly stole the code to present the activity view controller from this sample.

Here’s the updated code snippet that replaces the old usage of the UIActivityViewController in the original sample: 

NSURL *url = [NSURL URLWithString:[self.urlField text]];
NSArray *activityItemsArray = 
  [NSArray arrayWithObjects: myRenderer, printInfo, url, nil];
UIActivityViewController *activityViewController =
  [[UIActivityViewController alloc]
    initWithActivityItems:activityItemsArray
    applicationActivities:nil];


if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
  //iPad, present the view controller inside a popover
  if(!self.activityPopover){
    self.activityPopover =
      [[UIPopoverController alloc]
        initWithContentViewController:activityViewController];
  }
  if (![self.activityPopover isPopoverVisible]) {
    [self.activityPopover
      presentPopoverFromBarButtonItem:self.printButton
      permittedArrowDirections:UIPopoverArrowDirectionAny
      animated:YES];
  }else{
    //Dismiss if the button is tapped while pop over is visible
    [self.activityPopover dismissPopoverAnimated:YES];
  }
}else{
 //iPhone, present activity view controller as is
 [self presentViewController:activityViewController animated:NO completion:nil];
}

Running the modified app on an iPad and touching the action icon brings up the Share Sheet instead of the Printing UI directly (as it would have if I’d not modified the original sample). From there you can see that both the Print icon as well as the “Save PDF to iBooks” icon appears on the Share Sheet. And both behave as expected! 

Operation Mincemeat

If you’ve already got printing working in your app, now’s the time to update to using the Share Sheet method instead of the older method. Without a lot of extra work on your part, users get a great new option to save your application’s content in a print ready format. And if you’ve not added printing to your application to date, the “Save PDF to iBooks” feature provides yet another reason to do so.

Advertisements


1 Comment

Save PDF to iBooks

New in iOS 9 is the ability to save web pages as a PDF document to iBooks. It is a feature that is near and dear to my heart, in part because it is the last major feature I worked on at Apple before I left in early 2013.

What users of iOS 9 may notice is that Safari offers a new item in the Share Sheet: “Save PDF to iBooks”. They may think of this as a Safari specific feature.

iOS 9 Share Sheet

 

But the Photos app also allows this, as will every iOS app that presents printing using the iOS “Share Sheet” (in iOS programming parlance, using an ActivityViewController). If an app can print via the Share Sheet then “Save PDF to iBooks” automatically appears as well.

During my career at Apple I worked primarily on the printing subsystems of Mac OS X and iOS. Much of that work was on the software plumbing needed for users to print on paper. But a portion of that work was enabling the ability to produce a PDF file corresponding to what otherwise would have been printed output. Save as PDF from the print dialog was part of OS X since version 10.0 shipped in 2001.

When creating the printing system for iOS, allowing for the ability to create a PDF reproduction of the results of printing was a goal of the design from the beginning. To me it seemed almost more important to allow users to capture content to store on their device to take with them (or email or whatever) than it was to produce pieces of paper. The design of the printing system allowed for it but the user facing feature was left out. So in the summer of 2012 I worked on an early implementation of what is now shipping. And I’m thrilled that it’s now out.

Executives at Apple talk about the desire to “surprise and delight our users”. Well I experienced that firsthand when I got a notification from Apple’s “Tips” app on my iPhone soon after updating to iOS 9. For me the (ironic) surprise was that the tips app was telling me about a feature that I worked on. And I was delighted to see that it was finally shipping. 

Save PDF to iBooks Tip