Custom PDF Bookmark Provider in Swift for iOS
Use a custom bookmark provider using a plist
file. Get additional resources by visiting our guide on iOS PDF bookmark library.
//// Copyright © 2018-2025 PSPDFKit GmbH. All rights reserved.//// The Nutrient sample applications are licensed with a modified BSD license.// Please see License for details. This notice may not be removed from this file.//
import PSPDFKitimport PSPDFKitUI
class CustomBookmarkProviderExample: Example { override init() { super.init()
title = "Custom Bookmark Provider" contentDescription = "Shows how to use a custom bookmark provider using a plist file" category = .subclassing priority = 250 }
override func invoke(with delegate: ExampleRunnerDelegate) -> UIViewController { let document = AssetLoader.writableDocument(for: .annualReport, overrideIfExists: false) document.bookmarkManager?.provider = [BookmarkParser()]
let controller = PDFViewController(document: document) { $0.bookmarkSortOrder = .custom } controller.navigationItem.setRightBarButtonItems([controller.thumbnailsButtonItem, controller.outlineButtonItem, controller.searchButtonItem, controller.bookmarkButtonItem], for: .document, animated: false) return controller }}
class BookmarkParser: NSObject, BookmarkProvider { struct CustomBookmark: Codable { let identifier: String let pageIndex: PageIndex let name: String let sortKey: Int
private enum CodingKeys: String, CodingKey { case identifier case pageIndex case name case sortKey }
init(bookmark: Bookmark) { self.identifier = bookmark.identifier self.pageIndex = bookmark.pageIndex self.name = bookmark.name?.replacingOccurrences(of: "\"", with: "'") ?? String() if let sortKey = bookmark.sortKey { self.sortKey = sortKey.intValue } else { self.sortKey = 0 } }
func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(identifier, forKey: .identifier) try container.encode(pageIndex, forKey: .pageIndex) try container.encode(name, forKey: .name) try container.encode(sortKey, forKey: .sortKey) }
init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) identifier = try values.decode(String.self, forKey: .identifier) pageIndex = try values.decode(PageIndex.self, forKey: .pageIndex) name = try values.decode(String.self, forKey: .name) sortKey = try values.decode(Int.self, forKey: .sortKey) } }
var bookmarks: [Bookmark] { return self.bookmarkData }
var bookmarkData: [Bookmark]
override init() { self.bookmarkData = BookmarkParser.bookmarkDataFromFile() super.init() }
func add(_ bookmark: Bookmark) -> Bool { print("Add Bookmark: \(bookmark)") let index = bookmarkData.firstIndex(of: bookmark) if index == nil || index == NSNotFound { bookmarkData.append(bookmark) } else { bookmarkData[index!] = bookmark } return true }
func remove(_ bookmark: Bookmark) -> Bool { print("Remove Bookmark: \(bookmark)") if bookmarkData.contains(bookmark) { while let elementIndex = bookmarkData.firstIndex(of: bookmark) { bookmarkData.remove(at: elementIndex) } return true } else { return false } }
func save() { print("Save bookmarks.") let customBookmarks = bookmarkData.map({ return CustomBookmark(bookmark: $0) }) let jsonData = try? PropertyListEncoder().encode(customBookmarks) try? jsonData?.write(to: BookmarkParser.bookmarkURL()!, options: Data.WritingOptions.atomic) }
// MARK: Helpers
class func bookmarkURL() -> URL? { let applicationSupport = try? FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true) let fileURL = applicationSupport?.appendingPathComponent("customBookmarksProvider").appendingPathExtension("plist") return fileURL }
class func bookmarkDataFromFile() -> [Bookmark] { var bookmarkData = [Bookmark]()
guard let jsonData = try? Data(contentsOf: BookmarkParser.bookmarkURL()!) else { return bookmarkData }
let customBookmark: [CustomBookmark] = try! PropertyListDecoder().decode(Array.self, from: jsonData) bookmarkData = customBookmark.map({ let identifier = $0.identifier let pageIndex = $0.pageIndex let name = $0.name let sortKey = NSNumber(value: $0.sortKey) let action = GoToAction(pageIndex: PageIndex(pageIndex)) return Bookmark(identifier: identifier, action: action, name: name, sortKey: sortKey) })
return bookmarkData }}
This code sample is an example that illustrates how to use our SDK. Please adapt it to your specific use case.