Again: Easy way to request JSON with posting data - objective-c

I´m really new to Xcode and Objective-C ... I receive an JSON-Array to show the contents in my TableView:
in my -viewDidLoad:
[super viewDidLoad];
self.navigationItem.title = #"Kategorien";
NSURL *url = [NSURL URLWithString:#"http://www.visualstudio-teschl.at/apptest/kats.php"];
NSData *jsonData = [NSData dataWithContentsOfURL:url];
if(jsonData != nil)
{
NSError *error = nil;
_kategorien = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error];
}
and in my cellForRowAtIndexPath: method:
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSDictionary *data = [self.kategorien objectAtIndex:indexPath.row];
cell.textLabel.text = [data objectForKey:#"KAT_NAME"];
return cell;
It works great and shows my Strings (key -> KAT_NAME) in my Table.
But: How can i send a String with my request? For example i want to send "searchnumber = 2" with my request, like an ajax-request with javascript/jquery?
I searched but only could find examples that are to difficult (and maybe to oversized?) for my needs... is there no easy way like my request and ad something like "sendWithData" or is this method completely different with my simple request?

If you want to POST data, you'll need to create a NSMutableURLRequest object. You shouldn't use [NSData dataWithContentsOfURL:] anyway as this creates a synchronous request and blocks the UI. Even though it's a little more code, this is the right way to do it:
NSURL *url = [NSURL URLWithString:#"http://www.visualstudio-teschl.at/apptest/kats.php"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: url];
request.HTTPMethod = #"POST";
request.HTTPBody = [#"searchnumber=2" dataUsingEncoding: NSASCIIStringEncoding];
[NSURLConnection sendAsynchronousRequest: request
queue: [NSOperationQueue mainQueue]
completionHandler:
^(NSURLResponse *r, NSData *data, NSError *error) {
// do something with data
}];

Related

Trouble Storing URL in NSURL Variable

At the end of this method, "address" (NSURL variable) returns nil, even though "countyName" returns #"Alameda". What am I doing wrong?
-(void) getDataUrl {
if ([countyName isEqual: #"Alameda"]) {
NSURL * fileURL = [[NSURL alloc] initWithString:#"http://www.myservername.com/alameda.php"];
self.address = fileURL;
}
if ([countyName isEqual: #"Napa"]) {
NSURL * fileURL = [[NSURL alloc] initWithString:#"http://www.myservername.com/napa.php"];
self.address = fileURL;
}
if ([countyName isEqual: #"San Luis Obispo"]) {
NSURL * fileURL = [[NSURL alloc] initWithString:#"http://www.myservername.com/sanluisobispo.php"];
self.address = fileURL;
}
}
The solution here was to create a plist containing an array of dictionaries. Each dictionary held the name of a county and a url for the php script for that county. The plist was used to populate a tableview, and then the selected row was pushed to another tableview using the variable "countyName". "countyName" held the county name and the url. I then used countyName in my retrieveData method like this:
NSURL *url = [NSURL URLWithString:[countyName valueForKey:#"weburl"]];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLResponse *response = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
jsonArray = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
Thanks to everyone for taking the time to respond.

JSON returning data but data parameter is nil

I'm messing with some API stuff and tried the following:
#define searchWebService #"https://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=b667841296224aab0371a6f4a4546662&format=json&per_page=20&page=1"
// Construct url string for search
NSString *urlString = [NSString stringWithFormat:#"%#&text=%#&nojsoncallback=1", searchWebService, keyword];
//NSString *formattedURLString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
// Create url via formatted string
NSURL *url = [NSURL URLWithString:urlString];
// Get all data from the return of the url
NSData *photoData = [NSData dataWithContentsOfURL:url];
// Place all data into a dictionary
NSDictionary *allData = [NSJSONSerialization JSONObjectWithData:photoData options:kNilOptions error:nil];
Here is the URL that is built:
https://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=b667841296224aab0371a6f4a4546662&format=json&per_page=20&page=1&text=ball&nojsoncallback=1
When I plug this URL into a web browser I get formatted JSON but when I try to plug that into:NSData *photoData = [NSData dataWithContentsOfURL:url];
I get 'data parameter is nil'.
Any ideas what I'm doing wrong?
UPDATE:
I'm now using:
// Construct url string for search
NSString *urlString = [NSString stringWithFormat:#"%#&text=%#&nojsoncallback=1", searchWebService, keyword];
NSString *formattedURLString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]];
[NSURLConnection sendAsynchronousRequest:theRequest queue:nil completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
NSLog(#"BOOM %s %#", __func__, response);
if(!connectionError){
}
else{
NSLog(#"%s %#", __func__, connectionError.localizedDescription);
}
but neither of the logs ever show in the console.
UPDATE: TEST PROJECT
I've created a brand new project and put the following code in the viewDidLoad method:
NSString *urlString = [NSString stringWithFormat:#"%#", webServiceGetGlobalScores];
NSString *formattedURLString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:formattedURLString]];
NSLog(#"theRequest: %#", theRequest);
[NSURLConnection sendAsynchronousRequest:theRequest queue:nil completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
NSLog(#"BOOM %s %#", __func__, response);
if(!connectionError){
}
else{
NSLog(#"%s %#", __func__, connectionError.localizedDescription);
}
}];
And this is the defined #define webServiceGetGlobalScores #"http://www.appguys.biz/JSON/iTapperJSON.php?key=weBeTappin&method=getGlobalScores"
But still the sendAsynchronousRequest does not log anything.
UPDATE 3
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// Construct url string for search
NSString *urlString = [NSString stringWithFormat:#"%#", webServiceGetGlobalScores];
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSLog(#"urlRequest: %#", urlRequest);
[NSURLConnection sendAsynchronousRequest:urlRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{NSLog(#"BOOM %s %#", __func__, response);
NSLog(#"ERROR: %#", error);
if ([data length] > 0 && error == nil){
NSLog(#"BOOM");
}
}];
}
Try using + dataWithContentsOfURL:options:error: instead, which gives you an NSError object. Plus, notice that that the docs say:
Do not use this synchronous method to request network-based URLs. For
network-based URLs, this method can block the current thread for tens
of seconds on a slow network, resulting in a poor user experience, and
in iOS, may cause your app to be terminated.
Instead, for non-file URLs, consider using the
dataTaskWithURL:completionHandler: method of the NSSession class. See
URL Loading System Programming Guide for details.
You can use this code for downloading data,
NSString *urlString = //your whatever URL
NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]];
[NSURLConnection sendAsynchronousRequest:theRequest queue:nil completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
NSLog(#"%s %#", __func__, response);
if(!connectionError){
//Parse your JSON data
}
else{
NSLog(#"%s %#", __func__, connectionError.localizedDescription);
}
}];
Edited
Other way works!!! o_O see following
Now i surprised why following code working instead of above.
NSString *urlString = #"https://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=b667841296224aab0371a6f4a4546662&format=json&per_page=20&page=1&text=ball&nojsoncallback=1";
NSString *formattedURLString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSError *theErr;
NSData *thd = [NSData dataWithContentsOfURL:[NSURL URLWithString:formattedURLString] options:0 error:&theErr];
if(theErr){
NSLog(#"%#", theErr.localizedDescription);
}
else{
NSLog(#"%#", [[NSString alloc] initWithData:thd encoding:NSUTF8StringEncoding]);
}
3rd Way,
Newer iOS simulator version > 6.x having some issue. Reset your simulator and check it out your code.
For reference go NSURLConnection GET request returns -1005, "the network connection was lost"

NSURLSessionDataTask doesn't update my table

I have a class for get xml and parse it.
+ (instancetype)sharedRssNewsLoader
{
static BGMRssNewsLoader *sharedRssNewsLoader = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedRssNewsLoader = [[self alloc] initPrivate];
});
return sharedRssNewsLoader;
}
- (instancetype)initPrivate
{
self = [super init];
//did the superclass's designated initializer succeed?
if (self)
{
self.rssNewsItems = [[NSMutableArray alloc] init];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
_session = [NSURLSession sessionWithConfiguration:config
delegate:nil
delegateQueue:nil];
//RSS call
[self fetchFeed];
}
return self;
}
- (void)fetchFeed
{
NSString *requestString = #"http://xxx...";
NSURL *url = [NSURL URLWithString:requestString];
NSURLRequest *req = [NSURLRequest requestWithURL:url];
NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:req
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
self.rssXmlString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
DDLogVerbose(#"rssXmlString: %#", self.rssXmlString);
dispatch_sync(dispatch_get_main_queue(), ^{
[self parseXML:self.rssXmlString];
});}
];
[dataTask resume];
}
//parse XML
- (void)parseXML:(NSString *)source
{
NSError *error = nil;
DDXMLDocument *theDocument = [[DDXMLDocument alloc] initWithXMLString:source
options:0
error:&error];
NSArray *xmlItems = [theDocument nodesForXPath:#"//item"
error:&error];
for(DDXMLElement *itemElement in xmlItems)
{
NSString *itemTitleValue = [[[itemElement elementsForName:#"title"] lastObject] stringValue];
NSString *itemDescriptionValue = [[[itemElement elementsForName:#"title"] lastObject] stringValue];
BGMRssNewsEntry *rssEntry = [[BGMRssNewsEntry alloc] initRssNewsEntryWithTitle:itemTitleValue
rssText:itemDescriptionValue
rssUrl:nil
rssDate:nil
isRssRead:NO];
[self.rssNewsItems addObject:rssEntry];
}
}
//if a programmer calls [[BGMItemsStore alloc] init], let him know the error of his ways
- (instancetype) init
{
#throw [NSException exceptionWithName:#"Singleton"
reason:#"You are wrong! Use +[BBGMRssNewsLoader sharedRssNewsLoader]"
userInfo:nil];
return nil;
}
I would like to show result in the table. So in the other class I do:
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self)
{
//custom initialization
//initialization sharedRssNewsLoader with rssItems array and RSS call
[BGMRssNewsLoader sharedRssNewsLoader];
}
return self;
}
But my table is absolutely clear. I know that I need to switch to main_queue when I am working with NSURLSessionDataTask, I did it. I think that I mess with thread :(
That BGMRssNewsLoader is asynchronously loading the data and in parseXML you are adding entries to the self.rssNewsItems object. But I don't see where it tells the table to reload itself when this asynchronous request done. If your table view's data source tries to immediately retrieve the rssNewsItems array, it will likely be empty until the asynchronous request is complete.
Generally, when you're done loading the data, you'd call [self.tableView reloadData] (either by supplying the tableview as a parameter during the instantiation of the BGMRssNewsLoader, or by having it post a notification that the table view controller is observing, or by supplying a completion block parameter in which you'd perform reloadData).
I'd also suggest temporarily adding a breakpoint or log statement where you add objects to the rssNewsItems array, just to make sure that that the XML feed was successfully retrieved and parsed.
For example, I might retire fetch and define a method like so:
- (void)fetchFeedWithCompletionBlock:(void (^)(NSArray *items, NSError *error))completion
{
NSString *requestString = #"http://xxx...";
NSURL *url = [NSURL URLWithString:requestString];
NSURLRequest *req = [NSURLRequest requestWithURL:url];
NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:req completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
self.rssXmlString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
DDLogVerbose(#"rssXmlString: %#", self.rssXmlString);
dispatch_sync(dispatch_get_main_queue(), ^{
[self parseXML:self.rssXmlString];
if (completion) {
completion(self.rssNewsItems, nil);
}
});
}];
[dataTask resume];
}
I would, by the way, not have the initPrivate method initiate the fetch. You want the caller to be able to supply whatever completion block it wants.
I'd then have the tableview's controller do something like:
[[BGMRssNewsLoader sharedRssNewsLoader] fetchFeedWithCompletionBlock:^(NSArray *items, NSError *error) {
// use the items array and/or error
[self.tableView reloadData];
}];
Clearly, the fetchFeedWithCompletionBlock should actually detect errors and call the completion block with the appropriate NSError (so the table view can decide how it wants to present the error ... the singleton shouldn't be doing anything with the UI itself), but hopefully this illustrates the idea of a network object that provides a completion block to allow the caller to specify what it wants to do when the asynchronous request is complete.

Adding twitter search to a custom cell in tableview

I have a TWRequest that has created a dictionary output called dict. I would like to output the results of this into a custom cell in a tableView. The trick bit is that each individual tweet is then split (as its 'text' will be split into sections separated by colons) and put into an Array I am struggling with how to take the 'text' component out of the dictionary and apply this to the tableview. Here is my code... Any advise would be greatly appreciated. Thanks in advance.
- (void)fetchTweets
{
// Do a simple search, using the Twitter API
TWRequest *request = [[TWRequest alloc] initWithURL:[NSURL URLWithString:
#"http://search.twitter.com/search.json?q=myhashtagforsearchgoeshere"]
parameters:nil requestMethod:TWRequestMethodGET];
// Notice this is a block, it is the handler to process the response
[request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error)
{
if ([urlResponse statusCode] == 200)
{
// The response from Twitter is in JSON format
// Move the response into a dictionary and print
NSError *error;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&error];
NSLog(#"Twitter response: %#", dict);
}
else
NSLog(#"Twitter error, HTTP response: %i", [urlResponse statusCode]);
}];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"MyCell";
// I'm using a custom cell
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// then I want to grab the text part only from the tweets
NSString *tweetText = [[dict objectForKey:#"text"]objectAtIndex:indexPath.row];
// then I place the output into an array (the tweet is carved up into 3 components separated by a colon
NSArray *tweetComponents = [tweetText componentsSeparatedByString:#":"];
NSLog(#"Tweettext: %#", tweetText);
// then I link this to my labels in my custom cell using objectatindex
cell.myHeader.text = [tweetComponents objectAtIndex:0];
cell.myDetails.text = [tweetComponents objectAtIndex:1];
cell.myDate.text = [tweetComponents objectAtIndex:2];
return cell;
}

RaptureXML weird behaviour

I am getting an error (well it doesn't shows, just crashes out of app, no info on console)
that seems to happen whenever i call the method Iterate from RXML's rootXML:
-(void)valueSearch {
//FIRST CONNECTION
NSString *serverAddress = #"http://www.commix.com.br/clientes/grupoglobo/apple/valor.xml";
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:serverAddress]
cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
timeoutInterval:10];
NSError *requestError;
NSURLResponse *urlResponse = nil;
response = [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:&requestError];
//SECOND CONNECTION - Just an encapsulated form of the first, since i use it in other parts
// of the code
response = [self requestWithParameters:#"valor.xml"];
//i just uncommented both. but actually only one (connection) runs.
//Creation of the rooXML so i can grab the info i need
RXMLElement *rootXML = [RXMLElement elementFromXMLData:response];
//This array is where i'll keep the info from the files.
//it`s deallocated at the end in dealloc
searchResult = [[NSMutableArray alloc] init];
//This is the culprit. Atleast it seems so, since putting NSLog before and after
//revealed so.
[rootXML iterate:#"valor" usingBlock: ^(RXMLElement *valor) {
NSLog(#"valor: %#", [valor child:#"nome"].text);
[searchResult addObject:[valor child:#"nome"].text];
}];
}
The thing is, when i comment the requestWithParametersand use the normal non-encapsulated style (//FIRST CONNECTION) i don't get errors. But if i use the second, when the program reaches [rootXML iterate: [...]]it crashes there without warning.
using RaptureXML: https://github.com/ZaBlanc/RaptureXML
It also happens in another part of the code:
-(void)vehicleSearch {
NSString *path = [[NSBundle mainBundle] pathForResource:#"idArray" ofType:#"plist"];
NSMutableArray *idArray = [[NSMutableArray alloc] initWithContentsOfFile:path];
NSMutableString *serverAddress = (#"http://www.commix.com.br/clientes/grupoglobo/apple/modelo.php?marc=%#",[idArray objectAtIndex:0]);
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:serverAddress]
cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
timeoutInterval:10];
NSError *requestError;
NSURLResponse *urlResponse = nil;
response = [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:&requestError];
RXMLElement *rootXML = [RXMLElement elementFromXMLData:response];
searchResult = [[NSMutableArray alloc] init];
[rootXML iterate:#"modelo" usingBlock: ^(RXMLElement *modelo) {
NSLog(#"modelo: %#", [modelo child:#"nome"].text);
[searchResult addObject:[modelo child:#"nome"].text];
}];
[idArray release];
}
Happens at the same line [rootXML iterate:].
Sorry for leaks and stuff, i'm inexperienced (thats why i'm here), Thanks!
EDIT:
ACTUALLY the culprit is the line
NSMutableString *serverAddress = (#"http://www.commix.com.br/clientes/grupoglobo/apple/modelo.php?marc=%#",[idArray objectAtIndex:0]);
if i pass the parameter directly, without variables, it works:
NSMutableString *serverAddress = (#"http://www.commix.com.br/clientes/grupoglobo/apple/modelo.php?marc=4");
it shows correctly.
Are you sure that ,[idArray objectAtIndex:0] is an NSString?
Try to use
[NSString stringWithFormat:#"http://www.commix.com.br/clientes/grupoglobo/apple/modelo.php?marc=%#",[idArray objectAtIndex:0]];`
Or even
[NSString stringWithFormat:#"http://www.commix.com.br/clientes/grupoglobo/apple/modelo.php?marc=%#",[[idArray objectAtIndex:0]stringValue]];
response = [self requestWithParameters:#"valor.xml"];
if response is a property use self.response otherwise you will have memory leak issues.