Bookmarks and Custom Provider

Working with Bookmarks

For most use cases you do not need to interact with the bookmarks model yourself as PSPDFKit provides UI for the user to add, remove and sort bookmarks, depending on the configuration in use and automatically stores them in the PDF document they belong to. However when you want to provide either your own UI or your own bookmarks store, you need to access the model that PSPDFKit provides for you.

The bookmarks model essentially consists of two classes: PSPDFBookmark, which is the bookmark itself and contains informations about its name and what to do if a user selects a bookmark, and PSPDFBookmarkManager which is the container that keeps track of all the bookmarks of a document. You can access the bookmark manager through -[PSPDFDocument bookmarkManager]. This can then be used to retrieve, sort, add and remove bookmarks.

Accessing Bookmarks

To access bookmarks, there are two methods available. You can just call bookmarks on a bookmark manager to get a list of bookmarks. These bookmarks are unsorted and the order in this list may change at any point. This can be used if you want to know how many bookmarks there are or whether a particular bookmark is part of the bookmark manager. The other method is bookmarksWithSortOrder: which gets the sort order passed in and then returns a sorted array of bookmarks. Calling this method is unrelated to the bookmarks method. The returned array will be sorted according to the specified sort order, but this does not alter the state of the bookmark manager.

The available sort orders are PSPDFBookmarkManagerSortOrderCustom and PSPDFBookmarkManagerSortOrderPageBased. The sort order used by PSPDFKit when showing bookmarks to the user is set in -[PSPDFConfiguration bookmarkSortOrder], if you want to use the UI from PSPDFKit alongside with your own logic, make sure to use the same sort order, otherwise you may get unexpected results.

The custom sort order is managed by PSPDFKit internally. Adding bookmarks adds them to the end of the list and the user can manually sort them. The UI for sorting bookmarks is automatically provided by the bookmarks view controller when this is the sort option set in the configuration.

The page based sort order simply sorts bookmarks based on the page they are refering to. This only works for bookmarks that use a PSPDFGoToAction as their associated action. Convenience methods to access the page are available through a category defined by the go to action. Bookmarks that do not refer to a page will be sorted at the end of the list and the bookmark manager will make sure the sorting is always stable.

Adding and Removing Bookmarks

To add bookmarks, you simply call addBookmark: on the bookmark manager, passing in the bookmark that you want to add to the bookmark manager. If you instead want to remove a bookmark, you do so by calling removeBookmark:. If you want these changes to be stored, you need to save the associated document, which in turn will make sure the bookmark manager saves its content. By default, PSPDFKit automatically saves changes to a document at certain points.

Updating Bookmarks

PSPDFBookmark is an immutable object. If you want to make changes on a bookmark, you can create a mutable copy of this bookmark, change the part that you want to change, and then simply add that bookmark again by calling addBookmark:. The bookmark manager will replace the old bookmark with the new one in this case. For this to work it is important that you actually modify and existing bookmark. Do not create a new bookmark with e.g. the same page but a different name. Adding this bookmark will result in two bookmarks pointing to the same page but with different names.

Copy
1
2
3
4
5
let bookmarks = bookmarkManager.bookmarks(with: .pageBased)
if let first = bookmarks.first?.mutableCopy() as? PSPDFMutableBookmark {
    first.name = "First Bookmark"
    bookmarkManager.addBookmark(first)
}
Copy
1
2
3
4
5
6
NSArray<PSPDFBookmark *> *bookmarks = [bookmarkManager bookmarksWithSortOrder:PSPDFBookmarkManagerSortOrderPageBased];
PSPDFMutableBookmark *first = bookmarks.firstObject.mutableCopy;
if (first) {
    first.name = @"First Bookmark";
    [bookmarkManager addBookmark:first];
}

Implementing your own custom bookmark provider

Handling Bookmarks

If you want to use your own store to save bookmarks, this can be done by implementing your own PSPDFBookmarkProvider. Each bookmark manager has an array of bookmark providers and each bookmark provider participates in providing, adding and removing bookmarks. You can create your own bookmark provider and include it in the provider array on PSPDFBookmarkManager by simply setting the array.

The bookmark manager will then start calling out to your bookmark provider depending on its position on the list. The top most bookmark provider will get priority over the next one and so on. This is important for actions that manipulate the bookmark provider. Whenever the bookmark manager receives a call to addBookmark: or removeBookmark: it reaches out to its providers, starting with the first one in the array. The provider that feels responsible for consuming this call should return YES. At this point, further providers will not be called. For addBookmark: a bookmark provider could for example only take bookmarks with a certain type of action and return NO for others, so that these are then forwarded to the next bookmark provider in the list. When removeBookmark: is called, the provider should simply check if it owns this bookmark. If it does, it should remove it from its list and return YES, if it does not, it should return NO and give the next bookmark provider a chance to make the same check.

Persisting Bookmarks

A bookmark provider is responsible for loading and saving bookmarks. The bookmark manager will call save on a provider whenever a provider should persist its current state. Loading should be done by the provider during initialization or whenever any of the methods from PSPDFBookmarkProvider are called for the first time.

To properly persist a bookmark it is important that all of its properties are persisted. The preferred way to do this is to use PSPDFBookmark's support for NSCoding as this will also ensure that migration is done correctly in case new properties are introduced in the future. This can be easily achieved by using NSKeyedArchiver and NSKeyedUnarchiver.

If this is not suitable for your needs, you need to store the name and the action property as well as the identifier and sortKey properties. The latter are available only for this purpose and their values should never be altered or interpreted in any way. Make sure to check the changelog for changes in PSPDFBookmark to make sure that you also store new properties that may be needed in the future.

If you only want to store go to based bookmarks, you can also instead of storing the complete action just store the page index of that action and recreate the action when loading the bookmarks. In this case, make sure to keep the default provider in the list of providers to hand off other bookmarks to it, if you still want to support them.

There is PSCCustomBookmarkProviderExample in the catalog which implements this case and stores the bookmarks in a csv file.