I am retrieving JSON data from a URL that is formatted like so:
{"zoneresponse":
{"tasks":
[{"datafield1":"datafor1",
"datafield2":"datafor2",
"datafield3":"datafor3",...
}]
}}
I have no control over the structure as it is from a private API.
How do I insert data in a selected data field of an existing object?
I have tried this:
self.responseData = [NSMutableData data];
//testingURL is the api address to the specific object in tasks
NSURL *url = [NSURL URLWithString:testingURL];
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
[[[params objectForKey:@"zoneresponse"] objectForKey:@"tasks"] setValue:@"HelloWorld" forKey:@"datafield1"];
//HAVE TRIED setObject: @"" objectForKey: @"" as well
//*****PARAMS IS EMPTY WHEN PRINTED IN NSLog WHICH IS PART OF THE ISSUE - SETTING VALUE DOES NOT WORK
NSError * error = nil;
NSLog(@"Params is %@", params);
NSData *requestdata = [NSJSONSerialization dataWithJSONObject:params options:0 error:&error];
NSMutableURLRequest *request;
request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:@"POST"];
[request setValue:[NSString stringWithFormat:@"%d", [requestdata length]] forHTTPHeaderField:@"Content-Length"];
[request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
[request setHTTPBody:requestdata];
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if(conn) {
NSLog(@"Connection Successful, connection is: %@", conn);
} else {
NSLog(@"Connection could not be made");
}
The connection is being made but the dictionary params is empty when printed (the setValue is not displaying) and is not entering any data into the field I select.
I have checked these links but nothing explains whether it will insert into the right field and implies it will create a new object rather than update the existing one.
How to update data on server db through json api?
How to send json data in the Http request using NSURLRequest
Delegate methods
//any time a piece of data is received we will append it to the responseData object
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.responseData appendData:data];
NSError *jsonError;
id responseDict =
[NSJSONSerialization JSONObjectWithData:self.responseData
options:NSJSONReadingAllowFragments
error:&jsonError];
NSLog(@"Did Receive data %@", responseDict);
}
//if there is some sort of error, you can print the error or put in some other handling here, possibly even try again but you will risk an infinite loop then unless you impose some sort of limit
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
// Clear the activeDownload property to allow later attempts
self.responseData = nil;
NSLog(@"Did NOT receive data ");
}
//connection has finished, the requestData object should contain the entirety of the response at this point
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSError *jsonError;
id responseDict =
[NSJSONSerialization JSONObjectWithData:self.responseData
options:NSJSONReadingAllowFragments
error:&jsonError];
if(responseDict)
{
NSLog(@"%@", responseDict);
}
else
{
NSLog(@"%@", [jsonError description]);
}
//clear out our response buffer for future requests
self.responseData = nil;
}
The first method here states that data was received with "Did Receive data (null)", there is no error with connection however the final method prints the error message "JSON text did not start with array or object and option to allow fragments not set.", which is understandable because there is no data or object being sent.
How do I insert data into a selected field of an existing object?
You do this wrong:
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
[[[params objectForKey:@"zoneresponse"] objectForKey:@"tasks"] setValue:@"HelloWorld" forKey:@"datafield1"];
Your params
is empty dictionary and no object will be returned with [params objectForKey:@"zoneresponse"]
.
Try this:
NSMutableDictionary *params = [NSMutableDictionary new];
params[@"zoneresponse"] = @{@"tasks": @{@"datafield1": @"HelloWorld"}};
This will work but object for key @"tasks"
will be immutable. To add another objects to tasks
dictionary we need to make it mutable:
NSMutableDictionary *params = [NSMutableDictionary new];
NSMutableDictionary *tasks = [NSMutableDictionary new];
params[@"zoneresponse"] = @{@"tasks": tasks};
params[@"zoneresponse"][@"tasks"][@"datafield1"] = @"HelloWorld";
or
NSMutableDictionary *params = [NSMutableDictionary new];
params[@"zoneresponse"] = @{@"tasks": [@{@"datafield1": @"HelloWorld"} mutableCopy]};
Then you can add another objects to tasks
:
params[@"zoneresponse"][@"tasks"][@"datafield2"] = @"HelloWorld2";
params[@"zoneresponse"][@"tasks"][@"datafield3"] = @"HelloWorld3";
I think my answer will bring some clarity to operations with dictionaries for you.
Architectural context of your approach to sending JSON data to a servers database:
You are using an old method (since 03') of making a HTTP POST request to get your lovely JSON data in to the servers database. It is still functional and not deprecated and ultimately an acceptable approach. Typically how this approach works is you set up and fire a NSURLRequest with a NSURLConnection, the ViewController or object that fired the request typically implements the NSURLConnection protocol so you have a callback method that receives the NSURLRequests associated response as you've done. Theres also some NSURLCaching and NSHTTPCookStorage available to avoid redundancy and speed up the whole thing.
There is a new way (since 13'):
NSURLConnections successor is NSURLSession. Since Vladimir Kravchenko's answer focusses on forming the NSDictionary to be sent as a parameter who showed that you need to use a NSMutableDictionary instead of a static NSDictionary which can't be edited after it's init'ed. I'll focus on the networking approach surrounding your question though in order to be helpful.
/*
The advantage of this code is it doesn't require implementing a
protocol or multiple callbacks.
It's self contained, uses a more modern framework, less code
and can be just thrown in to the viewDidAppear
Basically - theres less faffing about while being a little easier to understand.
*/
NSError *requestError;
// session config
NSURLSessionConfiguration *configuration =
[NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session =
[NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
// setup request
NSURL *url = [NSURL URLWithString:testingURL];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
[request addValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
[request addValue:@"application/json" forHTTPHeaderField:@"Accept"];
[request setHTTPMethod:@"POST"];
// setup request parameters, Vladimir Kravchenko's use of literals seems fine and I've taken his code
NSDictionary *params = @{@"zoneresponse" : @{@"tasks" : [@{@"datafield1" : @"HelloWorld"} mutableCopy]}};
params[@"zoneresponse"][@"tasks"][@"datafield2"] = @"HelloWorld2";
params[@"zoneresponse"][@"tasks"][@"datafield3"] = @"HelloWorld3";
// encode parameters
NSData *postData = [NSJSONSerialization dataWithJSONObject:params options:0 error:&requestError];
[request setHTTPBody:postData];
// fire request and handle response
NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request completionHandler:
^(NSData *data, NSURLResponse *response, NSError *error)
{
NSError *responseError;
// parse response
NSDictionary *responseDict = [NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingAllowFragments
error:&responseError];
// handle response data
if (responseError){
NSLog(@"Error %@", responseError)
}else{
if (responseDict){
NSLog(@"Response %@", responseDict);
}
}
}];
[postDataTask resume];
Further Reading
The always wonderful Mattt Thompson has written about the transition from NSURLConnection to NSURLSession here:objc.io
You can find another similar Stack Overflow Questions here:Stack Overflow
If you would like a networking library that makes these issues a little easier check out Mattt Thompsons AFNetworking which can be found here:
GitHub
Further analysis on NSURLConnection versus NSURLSession can be found here:Ray Wenderlich