I have created a method that uses NSMutableURLRequest to request and receive JSON from a RESTful API running on a remote machine. The request successfully returns some JSON, but the returned sub-arrays in the JSON (see tasks: []) are empty.
The JSON returned from the API is:
{
"error":false,
"tasks":[
{
"id":1,
"task":"Get Milk",
"status":0,
"createdAt":"2014-08-09 12:29:15"
},
{
"id":2,
"task":"Call Mom",
"status":0,
"createdAt":"2014-08-09 12:46:52"
},
{
"id":3,
"task":"Call Dad",
"status":0,
"createdAt":"2014-08-09 12:47:04"
},
{
"id":4,
"task":"Study",
"status":0,
"createdAt":"2014-08-09 12:47:08"
}
]
}
The method that utilizies NSMutableURLRequest (proper parameters being passed is assumed) is:
-(NSDictionary *) apiRequestWithEndpoint:(NSString *) endpoint
withParams:(NSDictionary *) params
withHTTPHeaders:(NSDictionary *) headers
withHTTPMethod:(NSString *) method
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:endpoint]];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setHTTPMethod:method];
//set headers
for (id key in headers) {
[request setValue:[headers valueForKey:key] forHTTPHeaderField:key];
}
//set paramaters in request body
NSMutableString *requestBody = [[NSMutableString alloc] init];
int keyIndex = 0;
for (id key in params) {
if (keyIndex == 0) {
[requestBody appendFormat:#"%#=%#", key, [params valueForKey:key]];
} else {
[requestBody appendFormat:#"&%#=%#", key, [params valueForKey:key]];
}
keyIndex++;
}
[request setHTTPBody:[requestBody dataUsingEncoding:NSUTF8StringEncoding]];
NSURLResponse *response;
NSError *NSURLError = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&NSURLError];
if (NSURLError) {
NSLog(#"Error: %# Cancelling operation.", NSURLError);
return nil;
}
NSError *NSJSONSerializationError = nil;
NSDictionary *responseJSON = [NSJSONSerialization JSONObjectWithData:data
options:kNilOptions
error:&NSJSONSerializationError];
if (NSJSONSerializationError) {
NSLog(#"Error: %# Cancelling operation.", NSJSONSerializationError);
return nil;
}
NSLog(#"Data: %#",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
return responseJSON;
}
Notice the 4th last line that logs the returned NSData from the NSMutableURLRequest, the output is:
Data: {"error":false "tasks":[]}
The code that calls the above method:
NSString *getAllTasksEndpoint = #"http://developmentVPS.com/TaskList-API/v1/tasks";
NSString *apiKey = #"A Valid API Key";
NSDictionary *headers = [[NSDictionary alloc] initWithObjectsAndKeys:apiKey, #"Authorization", nil];
NSDictionary *responseJSON = [self apiRequestWithEndpoint:getAllTasksEndpoint
withParams:nil
withHTTPHeaders:headers
withHTTPMethod:#"GET"];
It is my understanding that the entire data returned from the request should be logged in that NSLog call, but "tasks" is empty. I am new to iOS development, so I may be missing something simple. Does anybody see anything I am missing here?
Related
Code:
+(id)loadJSONDataFromURL:(NSString *)urlString{
MsgsHelper *msg=[[MsgsHelper alloc]init];
// This function takes the URL of a web service, calls it, and either returns "nil", or a NSDictionary,
// describing the JSON data that was returned.
NSError *error;
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"GET"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
// Call the web service, and (if it's successful) store the raw data that it returns
NSURLResponse *response = nil; //
NSData *data = [ NSURLConnection sendSynchronousRequest:request returningResponse: &response error:&error ];
//here we get the respond from NSURLResponse and then we check for the statusCode //1
//200 is ok,, 0 is no internet connection else is server error //2
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;//1
//any warning like this one with integers shuold add the casting (int)
int statCode = (int)[httpResponse statusCode];//2
if(statCode == 200){
if (!data)
{
//NSLog(#"Download Error: %#", error.localizedDescription);
return nil;
}
// Parse the (binary) JSON data from the web service into an NSDictionary object
id dictionary = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
if (dictionary == nil) {
//NSLog(#"JSON Error: %#", error);
return nil;
}
return dictionary;
}else if(statCode == 0){
[msg alertStatus:NSLocalizedString(#"No internet", #"Message") :#"" :0 ];
return nil;
}else{
//Server Error
//NSLog(#"Server Error");
[msg alertStatus:NSLocalizedString(#"Server Error", #"Message") :#"" :0 ];
return nil;
}}
the problem on result as below:
results: (
{
"Name" = "\U0633\U0639\U0648\U062f \U0639\U0628\U062f\U0627\U0644\U0639\U0632\U064a\U0632 \U064a\U0646 \U062c\U062f\U064a\U062f";
"location" = CENTER;
}
)
maybe need to dataUsingEncoding to read arabic string, but how do it.
Thanks,
I have some JSON stored as an NSString and I am trying to convert this to a NSDictionary and get the value of 'EndDate' in my JSON.
I wanted to retrieve 'EndDate' from the JSON but due to the amount of levels i'm not quite sure how I should be achieving it.
Here is the JSON:
{ "GetResponse":{ "GetResult":{ "Faults":null, "Response":{ "Asset":{ "AssetParts":{ "#nil":"true" }, "CountryLookupCode":808, "Number”:24234, "Duplicate":"false", "Code":"`123”, "Channel”:”SR”, “Desc”:”Test”, "Number”:123, “Mandate”:{ "#nil":"true" }, “TestTime”:”True”, “Date”:”2010-08-12T19:00:00", “Results”:{ “Details”:[ { "EndDate":"2013-08-13T18:59:59", “Type”:”Taken”, "Item”:”902”, “Level”:”SL”, “Description”:”Timed”, “Group”:1, “Prov”:{ "#nil":"true" }, "StartDate":"2010-08-12T19:00:00" }, { "EndDate":"2013-08-13T18:59:59", “Machine”:”Dated”, “Country”:”UK”, "Code":"CCDD”, “Description”:”Addressed”, “Level”:2, "Provider":{ "#nil":"true" }, "StartDate":"2010-08-12T19:00:00" }, { "EndDate":"2013-08-13T18:59:59", "Type”:”Title”, "ItemNumber”:”1253”, "Service":"NEDD”, “Desc”:”Down”, “Grp”:5, "Provider":{ "#nil":"true" }, "StartDate":"2010-08-12T19:00:00" } ] } } } } } }
Here is my code:
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:#"https://192.168.0.20/test.json"]];
[request setHTTPMethod:#"GET"];
NSURLResponse *requestResponse;
NSData *requestHandler = [NSURLConnection sendSynchronousRequest:request returningResponse:&requestResponse error:nil];
NSString *requestReply = [[NSString alloc] initWithBytes:[requestHandler bytes] length:[requestHandler length] encoding:NSASCIIStringEncoding];
//NSLog(#"requestReply: %#", requestReply);
//End
NSData *data = [requestReply dataUsingEncoding:NSUTF8StringEncoding];
id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSArray *performersArray = [json objectForKey:#"GetResponse"];
for (NSDictionary *performerDic in performersArray) {
NSLog(#"%#", [performerDic objectForKey:#"EndDate"]);
}
Thanks
NSArray *detailsArray = json[#"GetResponse"][#"GetResult"][#"Response"][#"Asset"][#"Results"][#"Details"];
for (NSDictionary *detailsDict in detailsArray){
NSLog(#"%#",detailsDict[#"EndDate"]);
}
this should work
I'trying to get some random JSON from http://schematic-ipsum.herokuapp.com/ but I'm getting a response code 400.
Here is the code I'm using
+ (NSArray *)postData:(NSDictionary *)arguments toServer:(NSString *)urlString
{
NSArray *dataToReturn;
// if urlString is nil, we default it to our server
if(!urlString) urlString = JSON_SERVER;
// of course we need to turn the string into a valid array
NSURL *url = [NSURL URLWithString:urlString];
/*
prepare the post
*/
// we need to catch possible errors
NSError *error;
// turn our arguments into NSData
NSData *postData = [NSJSONSerialization dataWithJSONObject:arguments options:0 error:&error];
// we need the post' length
NSString *postLength = [NSString stringWithFormat:#"%lu", (unsigned long)[postData length]];
// create the url request
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:url];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded;charset=UTF-8" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
// here we'll check the server response
NSHTTPURLResponse *response = nil;
// here's our data from the server
NSData *urlData=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if ([response statusCode] >=200 && [response statusCode] <300)
{
// all good, let's see what we've got
NSString *responseData = [[NSString alloc]initWithData:urlData encoding:NSUTF8StringEncoding];
NSLog(#"Response ==> %#", responseData);
// parse the response into a friendly format
dataToReturn = [[NSArray alloc] initWithArray:[self returnJSONFromData:urlData]];
} else {
// somethin went wrong
NSLog(#"Response code: %ld", (long)[response statusCode]);
// check if it's our fault
if (error) {
NSLog(#"Server error: %#", [error localizedDescription]);
}
}
// return our formatted array or nil
return dataToReturn;
}
+ (NSArray *)returnJSONFromData:(NSData *)urlData
{
NSArray *dataToReturn;
NSError *e = nil;
NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData: urlData options: NSJSONReadingMutableContainers error: &e];
if (!jsonArray) {
NSLog(#"Error parsing JSON: %#", [e localizedDescription]);
dataToReturn = #[e];
} else {
dataToReturn = [[NSArray alloc] initWithArray:jsonArray];
NSLog(#"data from json: %#", dataToReturn);
}
return dataToReturn;
}
and I'm calling it like this, using a demo JSON from their website:
NSDictionary *post = #{ #"type": #"object", #"properties": #{ #"id": #{ #"type": #"string", #"ipsum": #"id" }, #"name": #{ #"type": #"string", #"ipsum": #"name" }, #"email": #{ #"type": #"string", #"format": #"email" } }};
[RetrieveDataFromServer postData:post toServer:#"http://schematic-ipsum.herokuapp.com/"];
You need to respect the syntax of "Form Data" that the server takes. To obtain this, you can use Google Chrome "Inspect Element", choose "Network" tab then do a request and you'll see this:
Look at "Form Data" section and you'll find out your problem, it's because you didn't pass the right structure to server so that the server doesn't understand.
I took the defaults parameters of server which is:
{
"type": "object",
"properties": {
"id": {
"type": "string",
"ipsum": "id"
},
"name": {
"type": "string",
"ipsum": "name"
},
"email": {
"type": "string",
"format": "email"
},
"bio": {
"type": "string",
"ipsum": "sentence"
},
"age": {
"type": "integer"
},
"avatar": {
"type": "string",
"ipsum": "small image"
}
}
}
So the data must be structured like this:
type:object
properties[id][type]:string
properties[id][ipsum]:id
properties[name][type]:string
properties[name][ipsum]:name
properties[email][type]:string
properties[email][format]:email
properties[bio][type]:string
properties[bio][ipsum]:sentence
properties[age][type]:integer
properties[avatar][type]:string
properties[avatar][ipsum]:small image
And don't forget to encode with percentage before sending it to server or you will fail again.
I tried to implement a method that take your dictionary and return the formatted form data, it works fine for this case but I'm not sure in a more general context. I'll post it here for you as a reference, it's really a mess, sorry for that but I don't have enough time for commenting.
- (NSString *)formatFormData:(NSDictionary *)dictionary
{
NSMutableArray *arrayPrefix = [NSMutableArray array];
NSMutableArray *arrayResult = [NSMutableArray array];
[self structureString:dictionary arrayPrefix:arrayPrefix arrayResult:arrayResult];
return [arrayResult componentsJoinedByString:#"&"];;
}
- (void)structureString:(NSDictionary *)dictionay arrayPrefix:(NSMutableArray *)arrayPrefix arrayResult:(NSMutableArray *)arrayResult
{
for(NSString *key in dictionay.allKeys)
{
NSObject *obj = [dictionay objectForKey:key];
if([obj isKindOfClass:[NSDictionary class]])
{
[arrayPrefix addObject:key];
[self structureString:(NSDictionary *)obj arrayPrefix:arrayPrefix arrayResult:arrayResult];
}
else
{
NSMutableString *string = [[NSMutableString alloc] initWithString:#""];
for(int i = 0; i < arrayPrefix.count; i++)
{
NSString *eachPrefix = arrayPrefix[i];
if(i == 0)
{
[string appendString:eachPrefix];
}
else
{
[string appendString:[NSString stringWithFormat:#"[%#]", eachPrefix]];
}
}
if(arrayResult.count == 0)
{
[string appendString:[NSString stringWithFormat:#"%#=%#", key, obj]];
}
else
{
[string appendString:[NSString stringWithFormat:#"[%#]=%#", key, obj]];
}
[arrayResult addObject:string];
}
}
}
And in your current method, add these lines:
- (NSArray *)postData:(NSDictionary *)arguments toServer:(NSString *)urlString
{
// omitted
// turn our arguments into NSData
NSData *postData = [NSJSONSerialization dataWithJSONObject:arguments options:0 error:&error];
NSString *stringTemp = [[NSString alloc] initWithData:postData encoding:NSUTF8StringEncoding];
stringTemp = [self formatFormData:arguments];
// encode form date before sending to server
stringTemp = [stringTemp stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
postData = [stringTemp dataUsingEncoding:NSUTF8StringEncoding];
// omitted
}
How can i disqualify lead in Microsoft Dynamics CRM ? Is there any particular API for doing this from iOS platform?
I tried this :
for (id key in [details allKeys]) {
if([key isEqualToString:#"LeadState"])
{
[contactPostDict setObject:#"2" forKey:#"State"];
}
else if([key isEqualToString:#"LeadStatus"])
{
[contactPostDict setObject:#"6" forKey:#"Status"];
}
}
This is the error:
"error": {
"code": "", "message": {
"lang": "en-US", "value": "Error processing request stream. The property name 'Status' specified for type 'Microsoft.Crm.Sdk.Data.Services.Lead' is not valid."
}
}
I have never worked on the iOS platform but from the error that is being thrown, the error seems to be pretty straight forward.
You are looking for the keys "Status" & "State" but they don't exist. Instead you should be looking at their schema names which is as follows
Status = statuscode, State = statecode
Can you try modifying the code to use the above keys and see if you still gets the same error.
(BOOL)setLeadStatus:(NSString *)ID andDetails:(NSMutableDictionary *)details
{
NSString *uri = [MSDYNAMICS_AUTHENTICATION_CALL stringByAppendingPathComponent:[NSString stringWithFormat:#"LeadSet(guid'%#')/",ID]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:uri]];
request.HTTPMethod = #"MERGE";
[request setValue:JSON_CONTENT_TYPE_PARAMETER forHTTPHeaderField:#"Content-Type"];
[request setValue:JSON_CONTENT_TYPE_PARAMETER forHTTPHeaderField:#"Accept"];
NSMutableDictionary *contactPostDict=[[NSMutableDictionary alloc]init];
for (id key in [details allKeys]) {
if([key isEqualToString:#"LeadState"])
{
[contactPostDict setObject:#"2" forKey:#"statecode"];
}
else if([key isEqualToString:#"LeadStatus"])
{
[contactPostDict setObject:#"6" forKey:#"statuscode"];
}
}
NSError *err;
NSData *postData=[NSJSONSerialization dataWithJSONObject:contactPostDict options:NSJSONWritingPrettyPrinted error:&err];
NSString* postString = [[NSString alloc] initWithData:postData encoding:NSUTF8StringEncoding];
//NSString *postString = #"";
[request setValue:JSON_CONTENT_TYPE_PARAMETER forHTTPHeaderField:CONTENT_TYPE_PARAMETER];
[request setHTTPBody:[postString dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES]];
SURLConnection *conn = [[SURLConnection alloc] init];
NSData *responseData = [conn sendSynchronousRequest:request returningResponse:nil error:nil];
NSString *err1 = [[NSString alloc]initWithData:responseData encoding:NSUTF8StringEncoding];
NSLog(#"error: %#",err1);
if(responseData.length==0 && [conn.response statusCode] == 204)
{
[self getLeadDetailsForID:ID];
return YES;
}
else{
NSString *err = [[NSString alloc]initWithData:responseData encoding:NSUTF8StringEncoding];
NSLog(#"error: %#",err);
}
return NO;
}
I get an self.usersArray with 2 elements in the format:
(
{
userCreated = "2012-01-05 12:27:22";
username = Simulator;
},
{
userCreated = "2013-01-01 14:27:22";
username = "joey ";
}
)
This is gotten in a completion block after which I call another method to fetch points for these 2 users through a helper class:
-(void)getPoints{
self.usersPointsArray = [[NSMutableArray alloc] init];
for (NSDictionary *usersDictionary in self.usersArray) {
[SantiappsHelper fetchPointsForUser:[usersDictionary objectForKey:#"username"] WithCompletionHandler:^(NSArray *points){
if ([points count] > 0) {
[self.usersPointsArray addObject:[points objectAtIndex:0]];
}
NSLog(#"self.usersPointsArray %#", self.usersPointsArray);
}];
}
}
The final self.usersPointsArray log looks like:
(
{
PUNTOS = 5;
username = Simulator;
},
{
PUNTOS = 2;
username = joey;
}
)
But the problem is that the way the call for points is structured, the self.usersPointsArray is returned twice, each time with an additional object, due to the for loop, I know.
Here is the Helper class method:
+(void)fetchPointsForUser:(NSString*)usuario WithCompletionHandler:(Handler2)handler{
NSURL *url = [NSURL URLWithString:#"http://myserver.com/myapp/readpoints.php"];
NSDictionary *postDict = [NSDictionary dictionaryWithObjectsAndKeys:usuario, #"userNa", nil];
NSData *postData = [self encodeDictionary:postDict];
// Create the request
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"POST"];
[request setValue:[NSString stringWithFormat:#"%d", postData.length] forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded charset=utf-8" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
__block NSArray *pointsArray = [[NSArray alloc] init];
dispatch_async(dispatch_get_main_queue(), ^{
// Peform the request
NSURLResponse *response;
NSError *error = nil;
NSData *receivedData = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
if (error) {
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
NSLog(#"HTTP Error: %d %#", httpResponse.statusCode, error);
return;
}
return;
}
NSString *responseString = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding];
pointsArray = [NSJSONSerialization JSONObjectWithData:[responseString dataUsingEncoding:NSASCIIStringEncoding] options:0 error:nil];
if (handler)
handler(pointsArray);
});
}
I cannot use the self.usersPointsArray with the initial objects, only with the finalized object. It wont always be 2 elements, i actually dont know how many it will be.
What would be the way to structure it so I get a final call when the self.usersPointsArray is complete and then I reload my tableview?
I think of your problem as a standard consumer-producer problem. You can create a queue count for the amount of items that will be processed (int totalToProcess=self.usersArray.count). Each time the completion handler is hit, it will do totalToProcess--. When totalToProcess reaches 0 you have processed all of the elements in your queue and can refresh your table.
If I understand your question correctly I believe this solves your problem. If not, hopefully I can with a bit more information.