2

I'm starting from scratch learning iOS programming.

I want my app to pull XML from a website. I'm thinking that to conform with the MVC pattern I should have a model class that simply provides a method to accomplish that (maybe have it parse the XML too and return an array).

Trouble is that all the tutorials I have found teach the NSURLSession in the context of the view and controller - so edit the appdelegate, or create a view controller, etc.

I got the following method from Apples documentation and I currently have it running as an IBAction when a button is pressed (so I can run it and test it easily). I'd like to get it working then put it in it's own class:

__block NSMutableData *webData;

NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];

NSURLSession *delegateFreeSession = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate: nil delegateQueue: [NSOperationQueue mainQueue]];
[[delegateFreeSession dataTaskWithURL: [NSURL URLWithString:url] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
    NSLog(@"Got response %@ with error %@.\n", response, error);
    NSLog(@"DATA:\n%@\nEND DATA\n", [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding]);
        webData = [[NSMutableData alloc] initWithData:data];
}
]resume];

My immediate question is:

Can someone explain how the completion handler is working and how to get data out of there? It's working, data is grabbing the xml from the website and logging it on the console, but copying it to webData doesn't work, it compiles but doesn't copy. (I'm still figuring out why the __block declaration allows webData to sneak in there in the first place!)

My bigger question would be if everyone thinks the idea of a separate model class for this process is a good idea. Is there a better way of designing this?

Thank you!

MayNotBe
  • 2,110
  • 3
  • 32
  • 47
  • Maybe you lookup . The answer there could give you a hint what is wrong. – Reinhard Männer Sep 19 '14 at 15:55
  • I've seen that page. I'm already using a delegate free session so I'm not sure how it's applicable ... also, my completion handler is executing, I simply don't know how to get the data out of it. – MayNotBe Sep 19 '14 at 16:33
  • What do you mean by "copy"? – Unheilig Sep 19 '14 at 17:00
  • I mean `webData = [[NSMutableData alloc] initWithData: data];` is not copying the information from `data` to `webData`. I want to pass by value and I thought that's what `initWithData` would do. – MayNotBe Sep 19 '14 at 17:06

1 Answers1

2

This may be just some confusion about how asynchronous blocks work. If you're doing this:

__block NSMutableData *webData;
// ...

[[delegateFreeSession dataTaskWithURL: [NSURL URLWithString:url] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
    NSLog(@"within the block, did I get data: %@", data);
    webData = [[NSMutableData alloc] initWithData:data];
}]resume];

NSLog(@"after the block, did I get data: %@", webData);

You might be seeing output that looks like this:

after the block, did I get data: (null)
within the block, did I get data: <NSData ...

What gives? Why did the code after the block run first? And where was the data? The problem is with our definition of "after". The NSLog that appears after the block actually runs before the block runs. It runs as soon as the dataRequest is started. The code inside the block runs after the request has finished.

Keeping the data result in a block variable local to that method does you no good. The value is uninitialized when you hit the end of the method. The block initializes it when it the block runs, but the value is discarded as soon as the block finishes.

Fix: do your handling of the data within the block. Don't expect it to be valid until after the block runs (which is well after the method runs):

EDIT - It's 100% fine to use self inside this block to call methods, set properties, etc. You need to watch out for retain cycles only when the block itself is a property of self (or a property of something self retains), which it isn't...

// don't do this
//__block NSMutableData *webData;
// ...

[[delegateFreeSession dataTaskWithURL: [NSURL URLWithString:url] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
    NSLog(@"within the block, did I get data: %@", data);
    NSMutableData *webData = [[NSMutableData alloc] initWithData:data];
    // do whatever you plan to do with web data
    // write it to disk, or save it in a property of this class
    // update the UI to say the request is done

    [self callAMethod:data];     // fine
    [self callAnotherMethod];    // fine
    self.property = data;        // fine
}]resume];

// don't do this, there's no data yet
//NSLog(@"after the block, did I get data: %@", webData);
danh
  • 62,181
  • 10
  • 95
  • 136
  • I was trying to NSLog it after(before) the block. What I don't understand is how I can save it in a property of the class if I can't use a class property in the block? Does that make sense? Sorry if this should be obvious but I'm REALLY new to this – MayNotBe Sep 19 '14 at 21:53
  • So, I've managed to parse `data` into an array of objects while in the block (didn't even need `webData`. Now how do I get that array out of the block and back to the caller? – MayNotBe Sep 19 '14 at 21:56
  • @MayNotBe - you can place any code you wish in that block. Anything that you'd normally put in a method is fine. See edit. – danh Sep 19 '14 at 22:12
  • thank you. I understand. I guess my trouble is even though I can do `[self.property = data]` , `self.property` is still empty when I need it and no matter how I try, I haven't figured out how to "catch up" as it were. – MayNotBe Sep 20 '14 at 00:18
  • Is the property declared `@property(strong, nonatomic) NSData *property;`? – danh Sep 20 '14 at 01:37
  • Thanks, I figured out I was going about the whole thing improperly. I've ended up using NSNotificationCenter to tell my ViewController when the array is populated. Thanks for your help! I'm sure I will have more questions. – MayNotBe Sep 27 '14 at 22:11