Reputation: 1594
I wrote the method below to handle the downloading of a file.
- (void)downloadFileFromURL:(NSURL *)url {
if (url != nil) {
if ([url pathExtension] != nil) {
RLMRealm *realm = [Utilities getRealm];
RLMResults<Download *> *results = [[Download allObjectsInRealm:realm] sortedResultsUsingKeyPath:@"id" ascending:NO];
Download *oldestDownload = [results firstObject];
Download *download = [[Download alloc] init];
download.id = oldestDownload.id+1;
download.name = [url path];
download.type = @"html";
download.state = DownloadStateNew;
download.token = [Utilities generateToken];
download.url = [url absoluteString];
[realm beginWriteTransaction];
[realm addObject:download];
self.current = download;
[realm commitWriteTransaction];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [[paths objectAtIndex:0] stringByAppendingPathComponent:download.name];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"text/plain", @"text/html", @"application/json", @"image/png", nil];
[manager GET:[NSString stringWithFormat:@"%@", url]
parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSData *data = [[NSData alloc] initWithData:responseObject];
[data writeToFile:path atomically:YES];
NSLog(@"successful download to %@", path);
RLMRealm *realm = [Utilities getRealm];
[realm beginWriteTransaction];
self.current.state = DownloadStateDone;
[realm commitWriteTransaction];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"Error: %@", error);
RLMRealm *realm = [Utilities getRealm];
[realm beginWriteTransaction];
self.current.state = DownloadStateCancelled;
[realm commitWriteTransaction];
}];
}
}
}
However, this keeps throwing an error that Realm was accessed from an incorrect thread. I don't know what's causing this though. Any ideas?
Upvotes: 0
Views: 396
Reputation: 15991
Judging from the AFNetworking code, it looks like both the success
and failure
blocks will be called on separate dispatch queues than the one that was originally used to kick off the request.
To guarantee ACID compliance, Realm objects are thread confined, and if you try and pass one to a different thread and then access any of its properties, that will trigger the exception you saw.
We recently added a feature in Realm Objective-C and Realm Swift where it's possible to pass thread-safe references representing Realm objects that you can use to then query for the same object on the new thread:
// Do work with `self.current`
RLMThreadSafeReference *currentReference = [RLMThreadSafeReference referenceWithThreadConfined:self.current];
AFHTTPRequestOperationManager *manager =[AFHTTPRequestOperationManager manager];
//Configure `manager`
[manager GET:[NSString stringWithFormat:@"%@", url]
parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject) {
MyCurrentClass *threadCurrent = [RLMRealm resolveThreadSafeReference:currentReference];
[threadCurrent.realm beginWriteTransaction];
threadCurrent.state = DownloadStateDone;
[threadCurrent.realm commitWriteTransaction];
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// Handle `threadReference` here too if needed
}];
Upvotes: 1