Greetings, traveler!
Working with documents is a common requirement in many iOS apps. Whether you’re displaying a PDF, showing a Quick Look preview, or handing the file off to the system for deeper interactions, developers today have multiple options available. Each framework offers different capabilities, limitations, and levels of control.
This article walks through four practical approaches to document presentation in SwiftUI, based on a simple demo that integrates Quick Look, PDFKit, WKWebView, and UIDocumentInteractionController. The goal is to compare these tools and show how they can be integrated cleanly into a modern SwiftUI application.
Quick Look Preview
Quick Look remains the fastest way to present a document using the system’s native interface. It supports many file types, opens almost instantly, and requires minimal configuration.
In SwiftUI, you can present a Quick Look sheet with .quickLookPreview(_:):
.quickLookPreview($url)All you need is a URL? that becomes non-nil to trigger the preview:
@State var url: URL?
Button("Quick Look Preview") {
url = Bundle.main.url(
forResource: "lorem_ipsum",
withExtension: "pdf"
)
}Quick Look is ideal when you want a clean, system-level viewer with no customization. It supports PDFs, images, text files, Office documents, and more.
PDFKit Viewer
When you need more control over how a PDF is displayed, PDFKit is the native solution. It exposes features such as page navigation, zooming behavior, display modes, and text selection.
A SwiftUI wrapper can be created using UIViewRepresentable:
struct PDFKitView: UIViewRepresentable {
let url: URL
func makeUIView(context: Context) -> PDFView {
let pdfView = PDFView()
pdfView.autoScales = true
pdfView.displayMode = .singlePageContinuous
pdfView.displayDirection = .vertical
pdfView.document = PDFDocument(url: url)
return pdfView
}
func updateUIView(_ pdfView: PDFView, context: Context) {}
}Note: You can use Data to create a document:
let data = Data()
let document = PDFDocument(data: data)This gives you a scrollable, auto-scaling PDF reader that fits naturally into a SwiftUI layout. It’s the right choice when the document type is strictly PDF and you need a rich reading experience.
Web View (WKWebView)
Some document formats that Quick Look can preview are also viewable inside a WKWebView. For PDFs, iOS automatically renders them through the built-in PDF engine. This can be useful when you want the viewer to live inside your app’s existing UI rather than launching a full-screen system sheet.
You can use a SwiftUI-native WebView with iOS 18.4+. For lower iOS versions you can create a simple wrapper like this:
struct WebKitView: UIViewRepresentable {
let url: URL
func makeUIView(context: Context) -> WKWebView {
let configuration = WKWebViewConfiguration()
let webView = WKWebView(frame: .zero, configuration: configuration)
let request = URLRequest(url: url)
webView.load(request)
return webView
}
func updateUIView(_ uiView: WKWebView, context: Context) {}
}Note: You can use Data to load a document:
webView.load(
data,
mimeType: mimeType,
characterEncodingName: characterEncodingName,
baseURL: baseURL
)WKWebView offers flexibility, but it’s not a general document viewer. Support varies by format, and its PDF rendering is much simpler than PDFKit.
To combine a legacy wrapper and a modern SwiftUI component, you can use something like this:
struct BackportedWebView: View {
let url: URL
var body: some View {
if #available(iOS 18.4, *) {
// On iOS 18.4 and later, assume a native SwiftUI WebView is available
WebView(url: url)
} else {
// Fallback to WKWebView wrapped in UIViewRepresentable
WebKitView(url: url)
}
}
}UIDocumentInteractionController
While somewhat older, UIDocumentInteractionController still provides a unique system-driven experience. It not only previews documents but also exposes additional interactions, such as sending the file to another app or printing it.
Because it’s a UIKit component, it needs to be wrapped using UIViewControllerRepresentable:
struct DocumentInteractionView: UIViewControllerRepresentable {
private var isPresented: Binding<Bool>
private let docController: UIDocumentInteractionController
private let controller: UIViewController = .init()
init(_ isPresented: Binding<Bool>, url: URL) {
self.isPresented = isPresented
self.docController = UIDocumentInteractionController(url: url)
}
func makeUIViewController(context: Context) -> UIViewController {
controller
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
guard isPresented.wrappedValue, docController.delegate == nil else { return }
docController.delegate = context.coordinator
docController.presentPreview(animated: true)
}
func makeCoordinator() -> Coordinator {
Coordinator(parent: self)
}
final class Coordinator: NSObject, UIDocumentInteractionControllerDelegate {
private let parent: DocumentInteractionView
init(parent: DocumentInteractionView) {
self.parent = parent
}
func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController {
parent.controller
}
func documentInteractionControllerDidEndPreview(_ controller: UIDocumentInteractionController) {
controller.delegate = nil
parent.isPresented.wrappedValue = false
}
}
}This component is not as flexible as PDFKit or Quick Look, but it can provide some additional options, such as opening a document through another application. However, it seems that it is the least preferred option these days.
Putting It All Together
The demo groups all these approaches into a simple SwiftUI view. We can use a sheet or fullScreenCover modifiers to present WebView and PDFKitView. And since the UIDocumentInteractionController has specific presentation behaviour, we can use a background modifier to show it:
struct ContentView: View {
enum Viewer: String, Identifiable {
case web, pdf
var id: String { rawValue }
}
@State var urlBinding: URL?
@State var isDocPresented: Bool = false
@State var viewer: Viewer?
let url = Bundle.main.url(
forResource: "lorem_ipsum",
withExtension: "pdf"
)!
var body: some View {
VStack(spacing: 30) {
Button("Quick Look Preview") {
urlBinding = url
}
Button("PDF Viewer") {
viewer = .pdf
}
Button("Web Viewer") {
viewer = .web
}
Button("Document Interaction Viewer") {
isDocPresented.toggle()
}
}
.background {
DocumentInteractionView($isDocPresented, url: url)
}
.sheet(item: $viewer) { viewer in
switch viewer {
case .web:
WebView(url: url)
case .pdf:
PDFKitView(url: url)
}
}
.quickLookPreview($urlBinding)
}
}Conclusion
iOS provides several powerful tools for presenting documents, and choosing the right one depends on the needs of your application:
- Quick Look — system-native preview for many formats.
- PDFKit — full-featured PDF rendering and navigation and extended customization options.
- WKWebView — lightweight in-app display for supported formats.
- UIDocumentInteractionController — system preview plus extended actions and specific presentation behaviour. The least preferred option today.
Each approach has its place, and combining them gives your app a complete document-handling toolkit.
