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.
 
  
  
  
 