user4992124
user4992124

Reputation: 1594

Realm accessed from wrong thread

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

Answers (1)

TiM
TiM

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

Related Questions