0

I have a category for my NSURL to set my DataModel for accessing my model objects later. Here, my DataModel is subclassed of my Core Data entity (NSManagedObject)


@implementation NSURL (CustomizedObject)

static char PROPERTY_KEY;

@dynamic model;

- (void)setChunk:(DataModel *)model {
    objc_setAssociatedObject(self, &PROPERTY_KEY, model, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (DataModel *)model {
    return (DataModel *)objc_getAssociatedObject(self, &PROPERTY_KEY);
}

@end

So, i'm downloading my data through NSURLSessionDownloadTask


for (DataModel *model in models) { // Here 985 URLs are there
    if ([model.status integerValue] == 0) {
        NSURL *requestURL = [NSURL URLWithString:model.downloadSequence];
        [requestURL setModel:model];
        NSURLSessionDownloadTask *downloadTask = [self.session downloadTaskWithURL:requestURL];
        [downloadTask resume];

        [self.arrFileDownloadData addObject:downloadTask];
    }
}

And, i'm accessing my DataModel in every successful download,


- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
    self.downloadSuccessCount++;

    DownloadModel *model = downloadTask.originalRequest.URL.model;
    NSString *filePath = [model.content.filePath stringByAppendingPathComponent:[[model.streamingSequence lastPathComponent] stringByDeletingPathExtension]];

    NSError *error;
    NSFileManager *fileManager = [NSFileManager defaultManager];

    NSURL *destinationURL = [NSURL fileURLWithPath:filePath];

    if ([fileManager fileExistsAtPath:[destinationURL path]]) {
        [fileManager removeItemAtURL:destinationURL error:nil];
    }

    BOOL success = [fileManager copyItemAtURL:location toURL:destinationURL error:&error];

    if (success) {
        NSError *fetchError = nil;

        NSFetchRequest *fetch = [NSFetchRequest fetchRequestWithEntityName:ChunkCoreData];
        [fetch setPredicate:[NSPredicate predicateWithFormat: @"modelId == %@", model.modelId]];

        NSArray *isProductExist = [self.context executeFetchRequest:fetch error:&fetchError];

        if ([isProductExist count] != 0) {
            DataModel *fetchedModel = [isProductExist lastObject];
            fetchedModel.status = [NSNumber numberWithInt:2];

            // create writer MOC
            NSManagedObjectContext* _privateWriterContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
            [_privateWriterContext setPersistentStoreCoordinator:[[self managedObjectContext] persistentStoreCoordinator]];

            // create main thread MOC
            NSManagedObjectContext* _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
            _managedObjectContext.parentContext = _privateWriterContext;

            NSError *saveError = nil;
            if (![_privateWriterContext save:&saveError]) {
                NSLog(@"Couldn't save %@", saveError.localizedDescription);
            }
        }
    }
}

Everything, works fine except sometimes. I'm getting crash in,

NSURL *destinationURL = [NSURL fileURLWithPath:filePath];

to says that filePath is nil. Or the issue is with my NSURL category creation?

What i'm doing here wrong? Suggestions please.

Praveenkumar
  • 24,084
  • 23
  • 95
  • 173
  • How does the combination of `char` and `OBJC_ASSOCIATION_RETAIN_NONATOMIC` work? – Droppy Jul 21 '16 at 06:54
  • @Droppy Then, what else i've to give to access my `DataModel` to access through `NSURL` i'm very new to *Categories* – Praveenkumar Jul 21 '16 at 06:56
  • Yeah my mistake; looking at [this](http://stackoverflow.com/a/8733320/3588973) answer, that *is* how it's done with categories. I rarely use them as they just complicate things and I like *simple*. – Droppy Jul 21 '16 at 06:59
  • Why not check if `filePath` exists before setting `destinationURL`? – l'L'l Jul 21 '16 at 07:24
  • @l'L'l You mean, checking `filePath` as `nil` or with `NSFileManager` – Praveenkumar Jul 21 '16 at 07:27
  • I think you could do it either way (NSFileManager or filePath == nil) — NSFileManager would be the more preferable way I'd imagine. If it doesn't exist or is nil then return an error message perhaps. Do you have any idea of why it's returning nil? If not maybe `NSLog` `model.content.filePath`, or `model.streamingSequence`, it might give you some better idea of what is happening... – l'L'l Jul 21 '16 at 07:30
  • So, does my `NSURL` category is perfect. Or i've to create it in separate class with static key value like in this [answer](http://stackoverflow.com/a/14899909/940096) – Praveenkumar Jul 21 '16 at 07:32
  • Well the question is, why does filePath return nil in the first place? – l'L'l Jul 21 '16 at 07:33
  • Yeah. But, i'm very new to adding properties into `Categories` That's why i'm asking? :) Even, i've updated the question – Praveenkumar Jul 21 '16 at 07:34
  • Maybe try putting `if ([fileManager fileExistsAtPath:[filePath path]]) { [fileManager removeItemAtURL:filePath error:nil]; }` before `NSURL *destinationURL = [NSURL fileURLWithPath:filePath];` and see what it does... – l'L'l Jul 21 '16 at 07:40
  • may be there is space in "filePath". so it could not convert to Url and and it gets crash. – Rinku Jul 22 '16 at 06:47

1 Answers1

0

I know that NSURLSession does not handle subclasses of NSURLRequest; it was a disaster in iOS 7, borderline unusable in iOS 8, and still pretty buggy in iOS 9.

I think this is because the objects are serialized to a plist even when the requests are handled in-process. I suspect that you're hitting the same bugs that I did, in which case associated objects on NSURLRequest or NSURL are likely to be equally problematic.

As an alternative, here are some other techniques that might work better for you:

  • Use setProperty:forKey:inRequest: and property:forKey:inRequest: on the NSURLRequest object instead.
  • For non-background sessions, add associated objects to the task instead.
  • Create a dictionary that maps the URL request object to the data you want.

HTH.

dgatwood
  • 10,129
  • 1
  • 28
  • 49