Question

We've recently converted our code to use UIDocument instead of manipulating files on the file system directly, and we've encountered some performance issues as a result. We are wondering whether we are using this class incorrectly, if anyone else had these issues, and what are the common ways to address them.

Our app

We have a "shoebox app" that manages a bunch of documents, each consisting of multiple image files that can be quite heavy, a small metadata file and a small preview image. The user may have many documents on her device (1000+ documents). Each document's files are grouped in an directory and we use NSFileWrapper to read and write them.

When our app starts, it needs the metadata of all the documents in order to show a document index, and a subset of the preview images. More preview images are loaded as the user scrolls.

In order to get that information, we open all the documents, read their metadata and preview image if we need to, close them, and then open again on demand.

Problem #1: Slow loading time

It takes a lot of time to open all the documents and read their metadata. I think there are several factors contributing to this problem: - Each document open action is relatively slow - The open document blocks and the completion blocks are executed on the same queue, which makes the operation's latency very bad (my document is open, but the completion block has to wait for X open document blocks before it can run)

We thought about solving this problem using a separate index file, but this approach has the drawback that we will need to manage the metadata in two places and that we will need to keep it synched with the file system in case iCloud changes the files.

Problem #2: Threading

Each open document creates its own "File Access Thread". When we open many documents concurrently, the overhead crushes the app.

We solved this issue by synching the open operations using a semaphore. This approach has the drawback that it slows down the loading even more.

Questions

  1. Is there some fundamental problem with the way we are using UIDocument? If not:
  2. Has anyone encountered a similar loading time problem? What is the common way to solve it? Do other apps keep an index file?
  3. Is there a way to make UI document use a thread pool? If not, how do you control resource usage?

Thanks!

Was it helpful?

Solution

Ok, no good news here.

We tried consulting with friends in the industry, profiling UIDocument and using modified implementations of it that alter various aspects of its operation in order to see if we can improve its performance but to no avail.

My conclusion is that UIDocument is not suitable for this kind of usage - it is just not designed to support the latency and throughput requirements we have for open operations. UIDocument should only be used when you want to open a small number of files at any given moment (much like word processors and drawing apps). Admittedly, this is exactly what Apple's documentation says, but I guess we had to learn just how serious they were the hard way :)

We ended up using some "tricks" to improve the user experience, and will move away from UIDocument as soon as we can.

So my recommendation is that only if:

  1. Your app resembles a document based app in nature, meaning you will not have more than a few documents open at any given moment
  2. You do not need the information inside the documents in order to "discover" them and show them to the user, or can afford the overhead of managing a separate index file
  3. You really need the auto saving/undo/synchronization/iCloud abilities of this class

then use it. Otherwise consider other solutions.

A a side note which is not directly related to the question but I will add here as a public service: UIDocument's async model required some major changes in the app architecture when we moved from direct file access. If you plan on making this move, evaluate the work you will need to do carefully.

Good luck future programmers.

OTHER TIPS

The document classes have methods to read asynchronously. Have your tried that?

This really sounds like something more suited to Core Data or SQLite, for the metadata. At the very least, cache the metadata in Core Data (a single store for the entire app, not one per document).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top