0

I have a method to download a JSON result from a server regularly so I would like to learn how to input parameters as opposed to keep typing out the same method over and over!

Here is what I have so far:

-(void)downloadData:(NSString *)saveto downloadURL:(NSString *)URL parameters:(NSString *)params{}

It is mostly working ok, except where I am attempting to save my result. I wish to store my result in an array called "locations", I am trying to pass the name locations in the "saveto" NSString, but am not sure how to do so?

Originally I used:

locations =[[NSMutableArray alloc] init];

I would like to somehow pass the name of the array I wish to save to so like this?:

saveto = [[NSMutableArray alloc] init];

Example to run method:

[self downloadData:[NSString stringWithFormat:@"locations"] downloadURL:[NSString stringWithFormat:@"http://test.com:80/test/locations.php"] parameters:[NSString stringWithFormat:@"welcome=hi"]];

2 Answers 2

2

You haven't shown us how you're retrieving the data (synchronously or asynchronously?) nor how you're building that request (it's curious that params and saveto and URL are all string parameters).

But I'd suggest doing it asynchronously (since you never want to block the main queue). And in that case, you provide a "block" parameter that the caller can specify the block of code to run when the download is done.

So, you might have downloadData that looks something like:

- (void)downloadDataWithURL:(NSURL *)URL parameters:(NSDictionary *)parameters completion:(void (^)(NSArray *array, NSError *error))completion
{
    // build your request using the URL and parameters however you want

    NSURLRequest *request = ...;

    // now issue the request asynchronously

    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
        if (!data) {
            completion(nil, connectionError);
            return;
        }

        NSError *parseError = nil;
        NSArray *array = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
        completion(array, parseError);
    }];
}

And can be invoked as:

NSURL *URL = [NSURL URLWithString:@"http://test.com:80/test/locations.php"];
NSDictionary *parameters = @{@"welcome" : @"hi"};

[self downloadDataWithURL:URL parameters:parameters completion:^(NSArray *array, NSError *error) {
    if (error) {
        NSLog(@"downloadDataWithURL error: %@", error);
        return;
    }

    self.locations = array;
}];

FYI, I made the URL to be a NSURL object (to conform to common practice). I also made the parameters a NSDictionary (so that if you're making a GET request or POST request of type application/x-www-form-urlencoded, you can more easily do the necessary CFURLCreateStringByAddingPercentEscapes; if it's JSON request, this also makes it easier to make a proper JSON request).

But feel free to change the parameters URL and parameters to be whatever type makes sense for your particular implementation, but hopefully this illustrates the idea. Add an additional completion block parameter which will be the block of code that will be run when the download is done, and then the caller do whatever it wants with the results.

Sign up to request clarification or add additional context in comments.

4 Comments

This is perfect, thank you. Not only has it answered my question but your solution has enourmously reduced the size of my original code and made NSURL Requests and their returns much clearer to me. Thanks again!
Do you know how I could go about setting "self.locations = array;" to a global variable in place of local? I can set it to a local and then move it to global, but is there a way to do it one step? Currently when I change it to "locations = array" I get an error explaining I need to use __block which can only be used locally?
@zipie When you say "global", do you really mean global? (The use of globals is generally frowned upon.) In my example, I assumed you would have a class property (not a local variable) called locations defined, and that the completion block would update that (in one step). Yes, if you wanted to update a local variable, you have to define that local variable as __block, but that would only make sense if it was a synchronous method, which this isn't. If you update a class property, no __block is needed.
I have found the problem, my variable name was the same as the class I was running...! So it was overriding and causing errors. Renamed the class/variable and it works perfectly now. Thanks again!
1

You should not reinvent the wheel and you should use one of the frameworks like AFNetworking or RestKit to do this stuff for you. In addition to doing the work for you it gives you very powerful tools like reachability and error handling and most importantly it handles converting to and from JSON (and many other formats). When you use AFNetworking you get an NSDictionary called responseObject that you can use in many ways including setting an NSString. Most of the time you will simply use that dictionary as the dataSource for your UI so all of the work is done for you.

AFNetworking code looks like this:

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager GET:@"http://example.com/resources.json" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
    NSLog(@"JSON: %@", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
}];

As you can see in only a couple of lines of code you handle success, failure, and the creation of an object with all your info. No need for [[NSDictionary alloc] init]

This is one of the many many great tutorials on AFNetworking

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.