-
Notifications
You must be signed in to change notification settings - Fork 298
Deferred Attachment Downloading
Since document attachments can be arbitrarily large, you may want to skip downloading them during a pull replication, both to speed up the replication and to save bandwidth. Then of course you need a way to download those attachment(s) later if they're needed locally.
NOTE: Aug 2015: This feature isn't yet available in a release. It's likely to appear in version 1.2 but we're not guaranteeing it. The API is preliminary and subject to change. The implementation is not yet merged to the master branch; it's on a branch named
feature/lazy_attachments
. Work is being tracked as issue #79.
To skip downloading attachments, set the CBLReplication
property downloadAttachments
to NO
before starting the replication. This will cause this replicator to skip the content of all attachments.
CBLReplication* pull = [db createPullReplication: kRemoteURL];
pull.downloadAttachments = NO;
[pull start];
The metadata of the attachments is still available via CBLAttachment
objects (or the _attachments
property), as usual. But if an attachment hasn't been downloaded, its content
and contentURL
properties, and the openContentStream
method, will all return nil
. To quickly check whether an attachment's content is available without reading or opening it, use the new boolean contentAvailable
property.
CBLAttachment *att = [doc.currentRevision attachmentNamed: @"bigImage"];
UIImage* bigImage = nil;
if (att.contentAvailable) {
bigImage = [UIImage imageWithData: att.content];
}
self.imageView.image = bigImage ? bigImage : kPlaceholderImage;
If you need the content of an attachment, you request it from the pull replication by calling its -downloadAttachment:
method. The return value is an [NSProgress](https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSProgress_Class/)
object that can report progress and notify you when the attachment completes (or if it fails), and can be told to cancel the download.
self.progress = [pullReplication downloadAttachment: att];
[progress addObserver: self forKeyPath: @"fractionCompleted" options: 0 context: NULL];
(Note: The CBLReplication
doesn't have to be the exact same instance that was used to pull the document containing the attachment. Any pull replication with the same remote database URL will work.)
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context
{
NSProgress* progress = self.progress;
if (object == progress) {
dispatch_async(dispatch_get_main_queue(), ^{
NSError* error = progress.userInfo[kCBLProgressErrorKey];
if (error != nil || progress.completedUnitCount == progress.totalUnitCount) {
[progress removeObserver: self forKeyPath: @"fractionCompleted"];
self.progress = nil;
if (error != nil) {
[self downloadFailedWithError: error]; // your own method
} else {
[self downloadComplete]; // your own method
}
} else {
[self updateDownloadProgress: progress.fractionCompleted];
}
});
}
}
Note: You don't have to use key-value observing; it's just the idiomatic Cocoa way to do it. Instead you could just start an NSTimer, with an interval of about a second, and examine the state of the NSProgress object each time the timer fires.
[self.progress removeObserver: self forKeyPath: @"fractionCompleted"];
[self.progress cancel];
self.progress = nil;
If you want to later undo a download, or if for whatever reason you don't want the contents of an attachment anymore, call the new method -[CBLAttachment purge]
.
CBLAttachment *att = [doc.currentRevision attachmentNamed: @"bigImage"];
[att purge];
Some of these may be addressed before this feature appears in a release.
- There's not currently any way to pull some attachments automatically but not others. It's all or nothing.
- Multiple calls to download the same attachment will issue redundant downloads, wasting network bandwidth. (Only one copy will be saved to disk, though.)
- Attachment downloading isn't as fault-tolerant as regular replication: if there's no network connectivity or the server isn't reachable, the request will retry a few times but eventually fail after about 30 seconds.
- If you retry an interrupted download, it starts over from the beginning instead of where it left off.
There's no way yet to cancel or pause a download.There's no way to "un-download" an attachment, i.e. purge an attachment from local storage.