How to view a .pdf file in SwiftUI?

I’m trying to have one of my views show a .pdf file. I understand that this is possible using UIKit, but unsure how to translate this over to SwiftUI. If anyone can assist with what type of code I need to add (or, Chris, if you can create a tutorial video on how to do this), I would appreciate it. Thank you.

You need to have a UIViewRepresentable that wraps a PDFView. Something like this very simple example:

import SwiftUI
import PDFKit

struct PDFKitView: UIViewRepresentable {
    
    let pdfDocument: PDFDocument
    
    init(showing pdfDoc: PDFDocument) {
        self.pdfDocument = pdfDoc
    }
    
    //you could also have inits that take a URL or Data
    
    func makeUIView(context: Context) -> PDFView {
        let pdfView = PDFView()
        pdfView.document = pdfDocument
        pdfView.autoScales = true
        return pdfView
    }
    
    func updateUIView(_ pdfView: PDFView, context: Context) {
        pdfView.document = pdfDocument
    }
}

struct PDFUIView: View {
    
    let pdfDoc: PDFDocument
    
    init() {
        //for the sake of example, we're going to assume 
        //you have a file Lipsum.pdf in your bundle
        let url = Bundle.main.url(forResource: "Lipsum", withExtension: "pdf")!
        pdfDoc = PDFDocument(url: url)!
    }
    
    var body: some View {
        PDFKitView(showing: pdfDoc)
    }
}
3 Likes

Thank you, roosterboy. I’ll attempt to incorporate your recommendation into my code and see if I can get it to work. Appreciate the feedback, and I’ll let you know how it turns out. Merry Christmas!

I made my best attempt, but no joy. Please forgive my ignorance: thus far, I’ve only completed CWC+ modules 1 & 2, but was anxious to begin work on my project.

I’ve incorporated your sample code above into a SwiftUI view, as-is (assuming that this is the template). In a separate SwiftUI view, I added the following property:

var showMyDoc = PDFKitView(showing: PDFDocument)

In the body of the same SwiftUI view, I added:

showMyDoc.updateUIView( PDFView, context: Context)
*NOTE: for this line of code, “PDFView” and “Context” are input parameters.

It’s unclear what I should add for the above input parameters, and it’s also unclear where I actually add the name/extension of my pdf file. The answer is likely easy, but inexperience is blocking the way.

Thank you for your help!

Looks like you only saw the PDFKitView struct and not the example of how to use it that I included.

Here it is again…

1 Like

Totally missed the extra scroll down portion of your previous reply: sincerest apologies.

Here’s what I’ve done:

  1. Just to test if I can get this to work, I created a new project with one SwiftUI view file named “PDFUIView.swift”. In this file, I’ve added the following code:

import SwiftUI
import PDFKit

struct PDFUIView: View {
let pdfDoc: PDFDocument

init() {
    let url = Bundle.main.url(forResource: "ghostcube", withExtension: "pdf")!
    pdfDoc = PDFDocument(url: url)!
}
var body: some View {
    PDFKitView(showing: pdfDoc)
}

}
struct PDFKitView: UIViewRepresentable {

let pdfDocument: PDFDocument

init(showing pdfDoc: PDFDocument) {
    self.pdfDocument = pdfDoc
}

//you could also have inits that take a URL or Data

func makeUIView(context: Context) -> PDFView {
    let pdfView = PDFView()
    pdfView.document = pdfDocument
    pdfView.autoScales = true
    return pdfView
}

func updateUIView(_ pdfView: PDFView, context: Context) {
    pdfView.document = pdfDocument
}

}

struct PDFUIView_Previews: PreviewProvider {
static var previews: some View {
PDFUIView()
}
}

  1. I added my pdf file (“ghostcube.pdf”) to my project asset folder. I verified separately that the pdf opens (outside of Xcode).

  2. The projects builds successfully, but when Xcode attempts to unwrap the Optional value, it unexpectedly found nil.

Should I set this up differently (i.e. is it OK to have this code all within a single SwiftUI file)? Once we have a working model, I’ll pattern my other project in the same manner.

Thank you for your help.

Don’t put your PDF file in your Assets folder.

Hey Patrick, I was interested in this little exercise. Did you manage to get it to work in Preview mode? It wouldn’t play the game for me but worked fine in the simulator.

Oh, OK. Where should I place my PDF file?

You can just put it in your project like any file. Or, if you want to be more organized, you can create a folder for it; like Documents, maybe?

The static preview doesn’t show anything for me, but the live preview does. Which makes sense, since AFAIK it’s just running the simulator. (BTW, “game”? :stuck_out_tongue:)

1 Like

Oh it’s just an Australianism.

When something does not work, we say “it’s not playing the game”. :rofl:

(edit) The same goes for anything mechanical that refuses to start or operate as expected despite some serious encouragement like a hammer.

Oh, never heard that one. That goes right in my memory bank alongside “spit the dummy” as a cool Australianism.

OK. I’ve removed the PDF file from the “Assets” folder, and simply copied it over to my project folder (using Finder). To ensure I understand, I placed it in the project folding with my .xcodeproj file. Still getting a nil value with unwrapping the Optional String. Still doing this incorrectly?

Corey,

That’s not the correct way to add a file to the project Bundle. What you need to do is drag the file from your Finder location into the Xcode project and drop it in the Project Navigator panel on the left. You will get a window appearing on your screen where you need to check some options. Make sure that you check “Copy items as needed” and also make sure that the “Add to targets” is also checked for your project name. See the sample below.

1 Like

Success! That was the final missing ingredient. Thank you to everyone for your help!

1 Like

Patrick,

This is really helpful! thanks so much for the example.

I do have a question, in order to zoom into the pdf, I need to set pdfView.canZoomIn but it is Get only. How to I set it so the user can pinch to zoom?

Thanks!!
Blessings,
–Mark

@FoundationSoftware

Mark,

On an iOS device you get pinch and zoom for free.

Got some very good coding ideas from this thread. This works fine for displaying MyFile.pdf. But I want to implement a general PDF reader, and I’m having a difficult time passing a different file name into the PDFUIView structure. Tried several methods but can’t seem to make it work.

How can I pass a different file name to the reader?
How can I set the reader to start with a pdf page other than 0?
(I’d like the start page number to also be an input variable)

This is the Nav link I use to call the reader from the Main view. It’s within a NavigationView, so works ok.

NavigationLink {
     PDFUIView()
} label: {
     Image(systemName: "doc.richtext")
}

This is the reader I set up using the coding from this thread.

import SwiftUI
import PDFKit

struct PDFUIView: View {
    let pdfDoc: PDFDocument
    var pdfName = "MyFile"
    init() {
        let url = Bundle.main.url(forResource: pdfName, withExtension: "pdf")!
        pdfDoc = PDFDocument(url: url)!
    }
    var body: some View {
        PDFKitView(showing: pdfDoc)
    }
}
struct PDFKitView: UIViewRepresentable {
    let pdfDocument: PDFDocument
    init(showing pdfDoc: PDFDocument) {
        self.pdfDocument = pdfDoc
    }
    func makeUIView(context: Context) -> PDFView {
        let pdfView = PDFView()
        pdfView.document = pdfDocument
        pdfView.autoScales = true
        return pdfView
    }
    func updateUIView(_ pdfView: PDFView, context: Context) {
        pdfView.document = pdfDocument
    }
}

Hello,

Have you find a solution to dynamically showing pdf names? I’m stuck in the same place.

Thanks!

Hello,

Firstly I’m also a beginner so, if this is not best practice or anything, feel free to teach me better!
I found a solution that should work. You just need to adjust the PDFUIView like this.

struct PDFUIView: View {
    let pdfDoc: PDFDocument
    
    init(pdfFilename: String) {
        let url = Bundle.main.url(forResource: pdfFilename, withExtension: "pdf")!
        pdfDoc = PDFDocument(url: url)!
    }
    var body: some View {
        PDFKitView(showing: pdfDoc)
    }
}

Now you can pass in your variable by calling it with : PDFUIView(pdfFilename: “MyFile”)

Hope this helps!

Best wishes,
Marcel