owlswipe owlswipe - 1 year ago 334
Swift Question

How to create a PDF in Swift with Cocoa (Mac)

Xcode 7.3.2, Swift 2, Cocoa (Mac).

My app involves the user entering in some text, which can be exported to a PDF.

In the iOS version of my app, I can create the PDF relatively easily with the


let html = "<font face=\'Futura\' color=\"SlateGray\"><h2>\(title)</h2></font><font face=\"Avenir\" color=\"SlateGray\"><h4>\(string)</h4></font>"

let fmt = UIMarkupTextPrintFormatter(markupText: html)

// 2. Assign print formatter to UIPrintPageRenderer

let render = UIPrintPageRenderer()
render.addPrintFormatter(fmt, startingAtPageAt: 0)

// 3. Assign paperRect and printableRect

let page = CGRect(x: 10, y: 10, width: 595.2, height: 841.8) // A4, 72 dpi, margin of 10 from top and left.
let printable = page.insetBy(dx: 0, dy: 0)

render.setValue(NSValue(cgRect: page), forKey: "paperRect")
render.setValue(NSValue(cgRect: printable), forKey: "printableRect")

// 4. Create PDF context and draw

let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, CGRect.zero, nil)

for i in 1...render.numberOfPages {

let bounds = UIGraphicsGetPDFContextBounds()
render.drawPage(at: i - 1, in: bounds)


// 5. Save PDF file

path = "\(NSTemporaryDirectory())\(title).pdf"
pdfData.write(toFile: path, atomically: true)

, and
all do not exist on OS X. How can I do the exact same thing as I am doing with this iOS code (create a basic PDF from some HTML and write it to a certain file path as a paginated PDF) with Mac and Cocoa?

Answer Source

I was recently having this problem myself, and I managed to come with a solution that works better than quemeful's because it automatically paginates the PDF. I think I have come up with the definitive way of creating a paginated PDF in a Swift app. And it's not as complicated as it seems (the following is based on Swift 2):

Step 1: Set up your App Sandboxing correctly. Every submitted Mac app requires App Sandboxing correctly, so it's best to just take 30 seconds to set it up right now. If it's not on already, go to your Target's settings and then the Capabilities header. Turn on App Sandboxing, up at the top. You should see many sub-capabilities, enable any one of these your app needs, and make sure to have every type of file access set to Read/Write.

Step 2: Add this function to your code, which will create an invisible webview and add your text into it.

func createPDF(fromHTMLString: String) {
            self.webView.mainFrame.loadHTMLString(htmlString, baseURL: nil)
            self.delay(1) {

Step 3: Create the delay() function to allow Xcode to wait a second for the HTML to be loaded into the web view.

 func delay(delay:Double, closure:()->()) {
            Int64(delay * Double(NSEC_PER_SEC))
        dispatch_get_main_queue(), closure)

(Note: For the Swift 3 version of this function, go here.)

Step 4: Add the declaration of our WebView above the function you added in step 2.

var webView = WebView()

Step 5: You may notice that we haven't created the MyCreatePDFFile() function yet. Turns out that there is no way to convert this WebView to a PDF with Swift, so we're going to have to turn to Objective-C (groan). Yep, you're going to have to run some Objective-C in your Swift app.

Step 6: Create a .m file by going to File -> New -> File (or by hitting CMD + N) then double-clicking on Objective-C File. Name it CreatePDF.m and leave it as an Empty File.

Step 7: When adding your .m file, you should get a prompt to add a bridging header:

enter image description here

Click Yes.

If you did not see the prompt, or accidentally deleted your bridging header, add a new .h file to your project and name it <#YourProjectName#>-Bridging-Header.h

In some situations, particularly when working with ObjC frameworks, you don't add an Objective-C class explicitly and Xcode can't find the linker. In this case, create your Bridging Header .h file named as mentioned above, then make sure you link its path in your target's project settings like this:

enter image description here

Step 8: Create a .h file by going to File -> New -> File (or by hitting CMD + N) then double-clicking on Header File. Name it CreatePDF.h.

Step 9: In your CreatePDF.h file, add the following code, which imports Cocoa and WebKit and sets up for your PDF creation function:

#ifndef CreatePDF_h
#define CreatePDF_h

#import <Cocoa/Cocoa.h>
#import <WebKit/WebKit.h>

@interface Thing : NSObject

void MyCreatePDFFile(WebView *thewebview);


#endif /* CreatePDF_h */

Step 10: Time to setup the .m file for the PDF creation function. Start by adding this to the empty .m file:

#import "CreatePDF.h"
#import <Cocoa/Cocoa.h>
#import <WebKit/WebKit.h>

@implementation Thing

void MyCreatePDFFile(WebView *thewebview) {


Step 11: Now you can add the code to the MyCreatePDFFile(..) function. Here's the function itself (this goes inside the currently-empty void function):

NSDictionary *printOpts = @{
    NSPrintJobDisposition: NSPrintSaveJob // sets the print job to save the PDF instead of print it.
NSPrintInfo *printInfo = [[NSPrintInfo alloc] initWithDictionary:printOpts];    
[printInfo setPaperSize:NSMakeSize(595.22, 841.85)]; // sets the paper size to a nice size that works, you can mess around with it
[printInfo setTopMargin:10.0];
[printInfo setLeftMargin:10.0];
[printInfo setRightMargin:10.0];
[printInfo setBottomMargin:10.0];
NSPrintOperation *printOp = [NSPrintOperation printOperationWithView:[[[thewebview mainFrame] frameView] documentView] printInfo:printInfo]; // sets the print operation to printing the WebView as a document with the print info set above
printOp.showsPrintPanel = NO; // skip the print question and go straight to saving pdf
printOp.showsProgressPanel = NO; 
[printOp runOperation];

Step 12: Now add this to your bridging header to allow your Swift file to see that Objective-C function you just made.

#import "CreatePDF.h"

Step 13: There shouldn't be any errors from this code left, but if there are please comment below about them.

Step 14: To call the createPDF function from anywhere your Swift file, do the following:


The string you see inside createPDF is HTML, and can be edited to anything HTML. If you don't know HTML, you can see some of the very basics right here.

That should be it! You should now be able to create paginated PDFs right from Swift, on Cocoa/Mac apps.

I really hope this helped you and I would appreciate an upvote because this was a ton of work. Comment below for anything.


Note: I also asked this question myself (in a broader way) and put this answer there as well. Nonetheless, this is (IMHO) the perfect answer for you.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download