I'm working on a project for school, and I was wondering if there was a way to actually update the connection with NSURLConnection if a user submits something. For example, I'm having twitter search based on username, and I have a default username set up when the view loads. What would I have to do to refresh that connection when a user enters a different username on that page? I have my button handlers all set up, and I'm using the same code as in my viewDidLoad function. Is there a method call or something that will actually reset the connection?
Thanks,
David
I attached example codes
// member variable 'conn'
// NSURLConnection *conn;
// #property(strong, nonatomic) NSURLConnection *conn;
-(void)viewDidLoad {
[super viewDidLoad];
[self sendRequest];
}
- (IBAction)sendButtonClicked {
[self sendRequest];
}
- (void)sendRequest {
if (self.conn) {
[self.conn cancel];
self.conn = nil;
}
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.google.com"]];
NSURLConnection *conn = [NSURLConnection connectionWithRequest:request delegate:self];
self.conn = conn;
[conn start];
}
// NSURLConnection Delegates
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// append received data
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
// error handling
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// using data
}
I don't think you can "reset" the connection. You will have to make a new request. In your button clicked method simply make a new request call with the new user name.
It works like a request in a browser, when you change some parameter in the url the browser sends of a new request to the web-server.
have a look at this example by apple (if you dont need the caching part just focus on URLCacheConnection.h/.m) http://developer.apple.com/library/ios/#samplecode/URLCache/Listings/Classes_URLCacheController_m.html#//apple_ref/doc/uid/DTS40008061-Classes_URLCacheController_m-DontLinkElementID_10
i
Related
I never use web API and don't know what i may read about this. I read FAROO Return Values doc, but i don't understand how i may get result-array (or dictionary) in cocoa.
Please anybody give me example or tutorial how to use Faroo API (or other web API) in objective-c.
Thank you.
To use web API and FAROO API in particular i use NSURLConnection class and NSURLConnectionDelegate protocol:
- (IBAction)search:(id)sender {
NSString* requestString = [NSString stringWithFormat:#"http://www.faroo.com/api?q=%#&start=1&length=10&l=ru&src=news&f=xml&YOUR_API_KEY",[searchField stringValue]];
// NSLog(#"str %#",requestString);
NSURL* requestUrl = [NSURL URLWithString:requestString];
NSURLRequest* searchRequest = [NSURLRequest requestWithURL:requestUrl cachePolicy:NSURLRequestReloadRevalidatingCacheData timeoutInterval:60];
[self performSelectorOnMainThread:#selector(startConnectionWithRequest:) withObject:searchRequest waitUntilDone:NO];
}
- (void)startConnectionWithRequest:(NSURLRequest*)request {
NSURLConnection* connection = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:YES];
if (connection) {
//update GUI and do something...
theData = [NSMutableData data];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSLog(#"Receive data");
[theData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
NSLog(#"Http status code %ld",(long)[httpResponse statusCode]);
}
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"Finish");
//do something with data and update GUI
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSAlert* searchFailedAlert = [NSAlert alertWithError:error];
[searchFailedAlert runModal];
}
One other way of doing things is to declare the missing method yourself as a category of the class in question. This will get the compiler to stop complaining about not finding the method, though of course you'll still need the runtime check you're already doing to avoid actually calling the method. You might also want to wrap such a declaration using availability macros, so that it will be ignored once you do move up to using the 10.5/10.6 SDK and you won't get a different compiler complaint down the line. That would look something like this:
#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4 //ignore when compiling with the 10.5 SDK or higher
#interface NSPropertyListSerialization(MissingMethods)
+ (NSData *)dataWithPropertyList:(id)plist format:(NSPropertyListFormat)format options:(NSPropertyListWriteOptions)opt error:(NSError **)error;
#end
#endif
I am relatively new to objective-c but struggling with delegates when it comes to NSURLConnection. Below I have an implementation file api.m
Elsewhere in my viewcontrollers I call this api object with the method getGroups and the purpose here is to return the number of groups found when the API request is made. I can see the data in the didReceiveData but how can I get this data back into my getGroups so that I can access it in my viewController?
In my view controller I have something like:
NSInteger *numGroups = [apiRequest getGroups];
and in my api.m implementation file I have the following. Again everything works I am just not sure how to return the data from didReceiveData back so I can access it in getGroups method.
#import "API.h"
#import "Constants.h"
#import "JSONParser.h"
#implementation API
#synthesize user, url, receivedData
-(NSInteger)getGroups {
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:10];
[request setValue:APIKEY forHTTPHeaderField:#"apikey"];
[request setHTTPMethod:#"GET"];
[request setURL:url];
NSURLConnection *myConnection;
myConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
//How do I access what was append'd in receivedData below
return 2;
}
/* ----------------------------------------------------------------------------------------
NSURLConnection Delegates
------------------------------------------------------------------------------------------- */
// Check the response code that was returned
- (NSInteger)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
return [httpResponse statusCode];
}
// Take a peak at the data returned.
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSLog(#"DATA: %#", [data description]);
//How to get this information back up into the getGroups method
[receivedData appendData: data];
}
// Close the connection
- (void)connectionDidFinishLoading:(NSURLConnection*)connection {
NSLog(#"Connection Closed.");
}
#end
What you want to do is in your ViewController that is calling the API set the API's delegate to self. Then you need to add those delegate methods inside your ViewController, not use them out of the API. That way when the NSURLConnection tries to call one of the delegate methods it will be accessible within youre ViewController. You also want to make sure you add the delegate protocol inside your ViewController's .h file as well.
As a quick example your VC.h file will contain the following:
#interface ViewController : UIViewController <NSURLConnectionDataDelegate>
Then in your VC.m file you'd have the following methods:
/* ----------------------------------------------------------------------------------------
NSURLConnection Delegates
------------------------------------------------------------------------------------------- */
// Check the response code that was returned
- (NSInteger)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
return [httpResponse statusCode];
}
// Take a peak at the data returned.
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSLog(#"DATA: %#", [data description]);
//How to get this information back up into the getGroups method
[receivedData appendData: data];
}
// Close the connection
- (void)connectionDidFinishLoading:(NSURLConnection*)connection {
NSLog(#"Connection Closed.");
}
Now when your NSURLConnection tries to call didReceiveData it will be called inside your ViewController, not in the API.
As a side note I whole heartedly recommend taking #SK9's advice and make this an Async call to abstract it from the main thread.
NSURLConnection's sendSynchronousRequest:returningResponse:error:, see here, will return a data object. Do be sure you're happy to block the current thread like this. I'd prefer for this to be an asynchronous request, with a completion block to handle the return. More details on the page I referred to, but do make reading up on blocks a priority if this is new. The Short Practical Guid to Blocks might help.
i'm quite a beginner at cocoa an objective-c - so please forgive me a possibly trivial question.
I'm currently working on an XCODE-Project which is fetching some data via NSJSONSerialization and store it for further work.
At this step I'm going to encapsulate the progress of fetching the data into an class which has some setter for the needed parameters (url to fetch from and the layer which should be parsed into an array). In order to use this procedure im creating the method inside this class which creates a connection and a request and returns the array which should contain the data. After some tests I tried to create an instance of this class and called the method which starts to fetch the data.
My problem is, that after calling the method data_array from my new instance "block_stats"
and store the data in an array of the same type - the array is empty
table_data = [block_stats data_array];
The reason of this behavior is that the usage of the methods in (didReceiveResponse,didReceiveData,connectionDidFinishLoading) are working asynchron and the return of the data_array was done before the download was finished.
the method inside the class which contains the downloading part:
- (NSMutableArray *)data_array
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
if(data_array)
{
[data_array removeAllObjects];
} else {
data_array = [[NSMutableArray alloc] init];
}
NSURLRequest *request = [NSURLRequest requestWithURL:data_url];
connection = [NSURLConnection connectionWithRequest:request delegate:self];
if(connection)
{
webdata = [[NSMutableData alloc]init];
}
return data_array;
}
the IBAction in another view which creates the instance and calls the method to fetch the data
- (IBAction)refresh:(UIBarButtonItem *)sender {
KDJSONparser *block_stats = [[KDJSONparser alloc]init];
[block_stats setURL:[NSURL URLWithString:#"*JSONDATA_URL*"]];
[block_stats setLayer:#"blocks"];
table_data = [block_stats data_array];
}
I would be very glad if anyone could give some advice. It would be very nice if it would be as easy as possible to understand for me. Thanks in advance!
The solution to your problem lies in delegation (As the word suggests you will be appointing some one else to take action when a situation presents itself.)
You have already used this in the following piece of your code.
NSURLRequest *request = [NSURLRequest requestWithURL:data_url];
connection = [NSURLConnection connectionWithRequest:request delegate:self];
Here when you have set self as the delegate for your NSURLConnection you are telling the compiler to send you any appropriate messages related to the connection. These messages include didReceiveResponse,didReceiveData,connectionDidFinishLoading.
So let's implement these methods in your class and they will look something like this.
KDJSONParser.m
- (NSMutableArray *)fetchData
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSURLRequest *request = [NSURLRequest requestWithURL:data_url];
connection = [NSURLConnection connectionWithRequest:request delegate:self];
if(connection)
{
webdata = [[NSMutableData alloc]init];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
// This method is called when the server has determined that it
// has enough information to create the NSURLResponse.
// It can be called multiple times, for example in the case of a
// redirect, so each time we reset the data.
// receivedData is an instance variable declared elsewhere.
[webdata setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// Append the new data to receivedData.
// receivedData is an instance variable declared elsewhere.
[webdata appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[connection release];
//Parse the webdata here. Your array must be filled now.
[self parseTheJSONData];
}
-(void)parseTheJSONData
{
//Do your parsing here and fill it into data_array
[self.delegate parsedArray:data_array];
}
And in your other class add this line in you refresh method
block_stats.delegate = self;
and implement
-(void)parsedArray:(NSMutableArray *)data_array;
Whenever I create an NSURLConnection in a class I have, it always connects to the first URL connected to by that class. It has an ivar conn that the NSURLConnection is stored in, and here is the method that connects:
-(void)getMoreProblems
{
problemsPage++;
NSURL *url=[NSURL URLWithString:[NSString stringWithFormat:#"http://projecteuler.net/problems;page=%d",problemsPage]];
NSURLRequest *req=[NSURLRequest requestWithURL:url];
NSLog(#"%p",conn);
conn=[[NSURLConnection alloc] initWithRequest:req delegate:self];
NSLog(#"%p",conn);
}
I have checked by NSLoging the URL's description and the Connection's pointer that they are different, as well as telling the UIApplication to load the URL in safari. As far as I can tell, It tries to load the right page. I also tried both POST and GET, but it didn't make a difference. What might be causing this?
EDIT FOR ANYONE LOOKING AT THIS WITH A SIMILAR PROBLEM:
My problem ended up being that I did not reinitialize the NSMutableData I stored the connection data in after each page loaded.
This isn't really an answer, but it's too long for a comment. I can't see anything wrong with the code that you posted. I pasted your code for getMoreProblems into a new project and added the delegate methods necessary to look at the results -- as far as I can tell it worked fine. I can see in the resulting string, the problem numbers starting with 1 on the first page I receive (from the first call to getMoreProblems) and starting with problem 51 on the second call to getMoreProblems. The only thing I added to your getMoreProblems method was the if-else clause at the end. HEre is the code I used:
#synthesize window = _window,receivedData;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
problemsPage = 0;
[self getMoreProblems];
}
-(void)getMoreProblems {
problemsPage++;
NSURL *url=[NSURL URLWithString:[NSString stringWithFormat:#"http://projecteuler.net/problems;page=%d",problemsPage]];
NSURLRequest *req=[NSURLRequest requestWithURL:url];
NSLog(#"%p",conn);
conn=[[NSURLConnection alloc] initWithRequest:req delegate:self];
NSLog(#"%p",conn);
if (conn) {
self.receivedData = [NSMutableData data];
} else {
NSLog(#"The Connection Failed");
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSLog(#"%#",response.URL);
[self.receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSLog(#"In connection:didReceiveData:");
[self.receivedData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"Succeeded! Received %lu bytes of data",[receivedData length]);
NSString *page = [[NSString alloc] initWithData:self.receivedData encoding:NSUTF8StringEncoding];
NSLog(#"%#",page);
[self performSelector:#selector(getMoreProblems) withObject:nil afterDelay:5];
}
So, I can't reproduce your problem -- I'm guessing it lies elsewhere in some code that you didn't post.
Is there any way that I can have a method sleep until a notification is posted? This is for an asynchronous NSURLConnection. I cannot move to a synchronous connection for multiple reasons.
Methods cannot "sleep"; that only applies to threads. Just split the code that needs to wait out into another method and have that method called when the notification arrives.
- (void) doStuffBeforeConnection {
[self doPreConnectionStuff];
NSURL * url = [NSURL URLWithString:#"/U/R/L"];
NSURLRequest * request = [NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestReturnCacheDataElseLoad
timeoutInterval:0];
NSURLConnection * conn = [NSURLConnection connectionWithRequest:request
delegate:self];
return;
// We are now "waiting"...
}
- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[self nowDoStuffThatNeededToWait:response];
}
use addobserver and set a target class with selector to be fired on notification.
when you need to trigger, use postNotification with notificatonName.
there you go..!!