I am making one application in which one webservices need to be called, is it possible to call webservices related to JSON in background,when user press homebutton in iPhone
You can call asynchronous request using
NSMutableURLRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"Your webservice URL here"]];
[[NSURLConnection alloc]initWithRequest:request delegate:self];
where you can implement delegate methods
In your Button Method
//yourURL is the webservice URL
[self performSelectorInBackground:#selector(loadDataFromWebservice:) withObject:yourURL];
-(void) loadDataFromWebservice : (NSString *)strUrl
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSURL *url=[[NSURL alloc] initWithString:strUrl];
NSData *data = [NSData dataWithContentsOfURL:imgUrl];
//Do your manipulations with data
//If you want to update any UI with the webservice data
[self performSelectorOnMainThread:#selector(assignDataViews:) withObject:responseObject waitUntilDone:YES];
[pool release];
}
//Return any object which you are comfortable with. I returned NSArray
-(void)assignDataViews : (NSArray *) yourObject
{
//Do all your UI changes here
}
Related
I'm using 2 UICollectionView on one screen and fetching 2 different data sets from my api.
But the problem is that I can't resolve is differentiating the incoming data depending on which view requested.
Here's a code snippet of what I do in the UICollectionView:
- (void)viewDidLoad
{
[super viewDidLoad];
NSURL *headlineurl = [NSURL URLWithString:#"api1"];
headlinerequest = [NSURLRequest requestWithURL:headlineurl];
[[NSURLConnection alloc] initWithRequest:headlinerequest delegate:self];
NSURL *mostnewsurl = [NSURL URLWithString:#"api2"];
NSURLRequest *mostnewsrequest = [NSURLRequest requestWithURL:mostnewsurl];
[[NSURLConnection alloc] initWithRequest:mostnewsrequest delegate:self];
}
And this is the code from the delegate:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
//What should I do here ?
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)theData
{
//What should I do here ?
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
//What should I do here ?
}
Thanks a lot.
Your problem is pretty common when people start using NSUrlConnection delegates,
First thing you're setting the delegate of both views to the same object, which can work, but will need some hackery.
I recommend one of the following solutions:
Solution 1 (uses delegates, more work)
Create a new class, and give give it the NSURlconnection delegate protocol
and call it something like apiFetchDelegate
Then place your delegate methods in there -(void) connectionDidFinishLoading, etc..
Now in your viewDidLoad method, change it to the following:
NSURL *headlineurl = [NSURL URLWithString:#"api1"];
headlinerequest = [NSURLRequest requestWithURL:headlineurl];
//Create a new instance of the delegate
apiFetchDelegate* headlineDelegate = [[apiFetchDelegate alloc] init];
[[NSURLConnection alloc] initWithRequest:headlinerequest delegate:headlineDelegate];
And the second delegate:
NSURL *mostnewsurl = [NSURL URLWithString:#"api2"];
NSURLRequest *mostnewsrequest = [NSURLRequest requestWithURL:mostnewsurl];
//Create second delegate
apiFetchDelegate* mostnewsDelegate = [[apiFetchDelegate alloc] init];
[[NSURLConnection alloc] initWithRequest:mostnewsrequest delegate:mostnewsDelegate];
now as you see, each one will get its own delegate, and data won't be mixed anymore !
Solution 2 (without delegates, less work to do)
This is probably a better solution for your need , I'm not sure why you need delegates for such a simple call, but if you don't , better go with this simple way !.
We will make async calls to avoid freezing the UI while data is being fetched, this will require an NSOperationQueue, here's how it'll work:
In your viewDidLoad method, change the code to this:
//Create your Queue here
NSOperationQueue *apiCallsQueue = [NSOperationQueue alloc] init];
[apiCallsQueue setMaxConcurrentOperations:2];
NSURL *headlineurl = [NSURL URLWithString:#"api1"];
headlinerequest = [NSURLRequest requestWithURL:headlineurl];
[NSURLConnection sendAsynchronousRequest:headlinerequest queue:apiCallsQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
//Here is your data for the first view
//
NSLog(#"Data for headline view: %#", [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding]);
}];
And for the second view:
NSURL *mostnewsurl = [NSURL URLWithString:#"api2"];
NSURLRequest *mostnewsrequest = [NSURLRequest requestWithURL:mostnewsurl];
[NSURLConnection sendAsynchronousRequest:mostnewsrequest queue:apiCallsQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
//Here is your data, for the second view
//
NSLog(#"Date for latest news: %#", [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding]);
}];
Let me know if this works for you or if you need further assistance.
I'm developing an iPhone application and have some trouble with my xml parser. I have to check multiple values from multiple XML files, but when the XML parser is active I can't do anything else. This is not how I want it, because checking the xml must be done in the background, without being noticed. Here is some of my code, hope it's enough!
appDelegate.datavalues = [[NSMutableArray alloc] init];
for(int i = 0; i < [headarray count]; i++){
NSMutableArray *infoarray = [[NSMutableArray alloc]initWithArray:[headarray objectAtIndex:i]];
NSString *IP = [infoarray objectAtIndex:1];
NSString *Unique = [infoarray objectAtIndex:2];
NSString *Port = [infoarray objectAtIndex:3];
NSString *relay = (NSString *)[infoarray objectAtIndex:4];
NSString *input = (NSString *)[infoarray objectAtIndex:5];
NSLog(#"relay%#",relay);
NSString *urlAddress = [NSString stringWithFormat:#"http://%#:%#/state.xml",IP,Port];
NSURL *url = [NSURL URLWithString:urlAddress];
NSString *authHeader = [NSString stringWithFormat:#"Basic %#",Unique];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval: 3];
[request setValue:authHeader forHTTPHeaderField:#"Authorization"];
//NSURLConnection *connectionResponse = [[NSURLConnection alloc] initWithRequest:request delegate:self];
NSURLResponse *myURLResponse;
NSError *myError;
NSData* myDataResult = [NSURLConnection sendSynchronousRequest: request returningResponse:&myURLResponse error:&myError];
NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithData:myDataResult];
XMLParser *parser = [[XMLParser alloc] initXMLParser];
//parser.relay = [infoarray objectAtIndex:4];
//Set delegate
[xmlParser setDelegate:parser];
//Start parsing the XML file.
BOOL success = [xmlParser parse];
after this I check some values so I don't think that's necessary to show!
You could refactor the XML-related code into a separate method and then you can use Grand Central Dispatch to run the method in background:
- (void) startOperation
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_NORMAL, 0), ^{
[self runSomeXMLChecks];
dispatch_sync(dispatch_get_main_queue(), ^{
// This is dispatched on the main queue so that
// you can update the UI. The NSLog is just an example.
NSLog(#"XML check done!");
});
});
}
Have a look at the NSOperation and NSOperationQueue APIs and/or the Concurrency Programming Guide. (Both are in the Xcode library).
From the docs:
The NSOperationQueue class regulates the execution of a set of
NSOperation objects. After being added to a queue, an operation
remains in that queue until it is explicitly canceled or finishes
executing its task. Operations within the queue (but not yet
executing) are themselves organized according to priority levels and
inter-operation object dependencies and are executed accordingly. An
application may create multiple operation queues and submit operations
to any of them.
I am calling NSURLConnection asynchronous method calls in my view controller. I would like to handle TWO RESPONSES FOR TWO REQUEST in the same Delegate. Please suggest me what would the best approach to achieve this? I'm developing in iOS 5 SDK.
UPDATED:
// Class A
[serverconns setDelegate:self];
connection = [serverconns executeAsyncHttpPost :firstjsonrequest];
[serverconns setDelegate:self];
connection = [serverconns executeAsyncHttpPost :secondjsonrequest];
- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
}
- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.appendData appendData:data];
}
- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
// logs the error
}
- (void) connectionDidFinishLoading:(NSURLConnection *)connection
{
NSData *responseData = [[NSData alloc] initWithData:appendData];
//HOW CAN WE HANDLE TWO RESPONSES FOR TWO REQUEST in the same Delegate
if (responseData)
{
// doing something
}
}
//Class B: ServerConnection
- (NSURLConnection *) executeAsyncHttpPost :(id) jsonParams
{
NSString *urlstr = [NSString stringWithFormat:#"%#", baseURL];
urlstr = [urlstr stringByAppendingFormat:method];
NSURL *pUrl = [NSURL URLWithString:urlstr];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:pUrl];
NSData *requestData = [NSData dataWithBytes:[jsonParams UTF8String] length:[jsonParams length]];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-type"];
[request setHTTPBody: requestData];
return [[NSURLConnection alloc] initWithRequest:request delegate:delegateResponder startImmediately:YES];
}
-(void) setDelegate:(id)newDelegate
{
delegateResponder = newDelegate;
}
save your connections somewhere (maybe ivar of your delegate)
- (void) connectionDidFinishLoading:(NSURLConnection *)connection
{
NSData *responseData = [[NSData alloc] initWithData:appendData];
//HOW CAN WE HANDLE TWO RESPONSES FOR TWO REQUEST in the same Delegate
if (responseData)
{
if (connection == yourFirstConnection) {
// doing something for first connection
} else {
// doing something for second connection
}
}
}
just point out some minor problem of your code
NSString *urlstr = [NSString stringWithFormat:#"%#", baseURL];
urlstr = [urlstr stringByAppendingFormat:method];
should replace to
NSString *urlstr = [baseURL absoluteString];
urlstr = [urlstr stringByAppendingString:method];
and add two(or more or array) weak/assign property of NSURLConnection to your class A (connection delegate)
#property (assign) NSURLConnection *myFirstConnection;
#property (assign) NSURLConnection *mySecondConnection;
// assume only need to handle two connection otherwise NSArray should be used instead
than in your class B (create connection)
- (NSURLConnection *) executeAsyncHttpPost :(id) jsonParams
{
NSString *urlstr = [baseURL absoluteString];
urlstr = [urlstr stringByAppendingString:method];
NSURL *pUrl = [NSURL URLWithString:urlstr];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:pUrl];
NSData *requestData = [NSData dataWithBytes:[jsonParams UTF8String] length:[jsonParams length]];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-type"];
[request setHTTPBody: requestData];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:delegateResponder startImmediately:YES];
delegateResponder.myFirstConnection = connection;
// delegateResponder.mSecondConnection = connection;
return connection;
}
If I were you I would create a CustomClass which inherits the NSURLConnection. And I will add property called tag.
When I initiate the CustomClass, I would set the tag property and use that to determine which request is being worked on
CustomURLConnection *connection = [[CustomURLConnection alloc] initWithRequest:request delegate:self tag:1];
- (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate tag:(int)_tag
{
if(self = [super initWithRequest:request delegate:delegate])
{
self.tag = _tag;
}
Now in the code you posted add this
- (void) connectionDidFinishLoading:(NSURLConnection *)connection
{
NSData *responseData = [[NSData alloc] initWithData:appendData];
//HOW CAN WE HANDLE TWO RESPONSES FOR TWO REQUEST in the same Delegate
if (responseData)
{
if (connection.tag == 1){
}
}
}
return self;
}
I think all the mentioned solutions are "ugly". I would not implement a solution with delegate methods but instead create a blocks-based solution. I could post an example if you're interested. I would make use of the AFNetworking classes for this approach.
What follows is an example of a class that handles 2 different responses without using a delegate implementation, opting for blocks instead with the AFNetworking library.
- (void)JSONFromService
{
// create the first request and set the methods that handle the return values (either NSData or NSError in this case) in blocks ...
NSURL *firstURL = [NSURL URLWithString:#"http://dl.dropbox.com/u/6487838/test1.html"];
NSURLRequest *firstRequest = [NSURLRequest requestWithURL:firstURL];
AFHTTPRequestOperation *firstOperation = [[AFHTTPRequestOperation alloc] initWithRequest:firstRequest];
[firstOperation setCompletionBlockWithSuccess:^ (AFHTTPRequestOperation *operation, id object)
{
NSString *firstString = [[NSString alloc] initWithData:object encoding:NSASCIIStringEncoding];
NSLog(#"%#", firstString);
} failure:^ (AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"%#", error);
}];
[firstOperation start];
// create the second request and set the methods that handle the return values (either NSData or NSError in this case) in blocks ...
NSURL *secondURL = [NSURL URLWithString:#"http://dl.dropbox.com/u/6487838/test2.html"];
NSURLRequest *secondRequest = [NSURLRequest requestWithURL:secondURL];
AFHTTPRequestOperation *secondOperation = [[AFHTTPRequestOperation alloc] initWithRequest:secondRequest];
[secondOperation setCompletionBlockWithSuccess:^ (AFHTTPRequestOperation *operation, id object) {
NSString *secondString = [[NSString alloc] initWithData:object encoding:NSASCIIStringEncoding];
NSLog(#"%#", secondString);
} failure:^ (AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"%#", error);
}];
[secondOperation start];
}
I usually subclass NSURLConnection and add properties to store whatever context I need to handle the response.
Since the delegate methods get NSURLConnection passed in, you can just cast it back to your subclass and access the context.
Take a look at this example.
I think you should keep all of your connections in an activeConnections array. Every time one finishes, you do [activeConnections indexForObject:connection] and you update your delegate method accordingly, using the index.
Now, a cleaner way to do it( and a better way from my point of view, but this depends on how large is the data you want to transfer) is to use queues. I'll provide a small example and add comments to it:
// we assume you have 2 requests: req1, req2
//now, create a new dispatch queue
dispatch_queue_t netQueue = dispatch_queue_create("com.mycompany.netqueue",DISPATCH_QUEUE_SERIAL);
//execute the operations in the queue ASYNC
//is very important to dispatch this ASYNC on a background thread, otherwise your UI will be stuck until the request finishes
dispatch_async(netQueue,
^{
// We are on a background thread, so we won't block UI events (or, generally, the main run loop)
NSHTTPURLResponse *response = nil;
NSError *error = nil;
NSData *data = nil;
//We can call the request synchronous and block this thread until completed
data = [NSURLConnection sendSynchronousRequest:req1
returningResponse:&response
error:&error];
dispatch_async(dispatch_get_main_queue(),
^{
//call your delegate with the appropriate method for req1
//be sure to copy the contents in data, as we will reuse it with the next request
});
//We can call the other request synchronous and block this thread until completed
data = [NSURLConnection sendSynchronousRequest:req2
returningResponse:&response
error:&error];
dispatch_async(dispatch_get_main_queue(),
^{
//call your delegate with the appropriate method for req2
});
//and this can go on forever. If you have many requests to execute, simply put them in a loop
});
dispatch_release(netQueue);
I have a NSURLConnection that gets data from a JSON web service, and everything works fine. I'm using it to post something to the server and get a success response.
After that call I want to initiate another NSURLConnection to refresh the data, so I'm doing so inside the connectionDidFinishLoading method, however this second connection isn't calling connectionDidFinishLoading when it is done loading.
Can I not initiate a NSURLConnection from inside the connectionDidFinishLoading method?
EDIT: Below is the code. I subclassed NSURLConnection to include a Tag NSString, calling the new class NSURLConnectionHelper. I'm using this to differentiate which connection has called the connectionDidFinishLoading.
- (void)connectionDidFinishLoading:(NSURLConnectionHelper *)connection
{
if([connection.Tag isEqual:#"NewMessage"]){
NSString *jsonString = [[NSString alloc] initWithData:receivedNewMessageData encoding:NSASCIIStringEncoding];
NSDictionary *results = [jsonString JSONValue];
[jsonString release];
[connection release];
if ([[results objectForKey:#"MessageAdded"] isEqual:#"True"]) {
User *newUser = [[User alloc] init];
[newUser retrieveFromUserDefaults];
if([newUser IsLoggedIn]){
Message *message = (Message *)[messages objectAtIndex: 0];
NSString *urlAsString = // url for webservice goes here
NSURL *url = [NSURL URLWithString:urlAsString];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
NSURLConnectionHelper *connection1 = [[NSURLConnectionHelper alloc] initWithRequest:request delegate:self];
connection1.Tag = #"GetLatestMessages";
[request release];
if (connection1) {
receivedLatestMessagesData = [[NSMutableData data] retain];
} else {
// Inform the user that the connection failed.
}
}
}
}else if([connection.Tag isEqual:#"GetLatestMessages"]){
//do some other stuff but this code is never reached
}
}
I'm not familiar with NSURLConnectionHelper but it looks like you're never starting the connection.
I ended up having a space in my web service url, once I corrected that it worked.
My application seems to have 4 memory leaks (on the device, running instruments).
The memory leaks seems to come from this code:
NSURL *url = [self getUrl:destination];
[destination release];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setHTTPMethod:#"GET"];
[request addValue:#"application/json" forHTTPHeaderField:#"content-type"];
NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:request delegate:self];
[request release];
[connection release];
EDIT: added code for getUrl
- (NSURL *)getUrl:(NSString *)actionUrl
{
NSString *rawUri = [[NSString alloc]initWithFormat:#"%#/%#", kBaseUrl, actionUrl];
NSURL *url = [[[NSURL alloc] initWithString:rawUri] autorelease];
[rawUri release];
return url;
}
I am releasing all my objects as far as I can see but it's still showing this as the source of the 4 memory leaks.
This is on the Device running 3.1.3
Is it acceptable to have a few memory leaks in your app or do they all have to go?
EDIT: I've added autorelease to getUrl. However it still shows up with memory leaks
EDIT2: The behaviour is rather strange. I launch the app and hit the button that makes this call once. 4 leaks are discovered. I press back and hit the button again, and keep doing this a few times, and still only 4 leaks. However, if I wait a few seconds and then press the button a gain a few more times, 9 leaks are discovered. It's not a small 128 byte leak, but it's 1.61KB at this point.
EDIT3: Here is the connectionDidFinishLoading
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
SBJSON *jsonParser = [[SBJSON alloc] init];
NSString *jsonString = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding];
[receivedData setLength:0];
[receivedData release];
[self.delegate dataReceived:[jsonParser objectWithString:jsonString]]; // See method below
[jsonParser release];
[jsonString release];
}
The delegate gets the data, then transforms it (and in return passes it on to another delegate once the product is constructed)
- (void)dataReceived:(id)data
{
NSMutableArray *myObjects = [[NSMutableArray alloc]init];
ObjectFactory *objectFactory = [[ObjectFactory alloc]init];
// Only one object
if ([data isKindOfClass:[NSDictionary class]])
{
Object *object = [objectFactory buildObject:data];
[myObjects addObject:object];
[object release];
}
// Multiple objects
if ([data isKindOfClass:[NSArray class]])
{
for (NSDictionary *objectSrc in data)
{
Object *object = [objectFactory buildObject:post];
[myObjects addObject:object];
[object release];
}
}
[objectFactory release];
[self.delegate objectsReceived:myObjects];
}
EDIT4:
Something I did notice is that the object "ConnectionObject" that contains the NSUrlConnection, never seem to be deallocated.
I put a breakpoint on dealloc which calls [connection release]
This dealloc is never called. All the deallocs are called down the chain except for this one.
I tried [connection cancel] in the "connectionDidFinishLoading" call to see if that helped but not at all.
This sure is a mystery to me...
You are releasing something you shouldn't:
NSURL *url = [self getUrl:destination];
// the returned url should have been autoreleased by the getUrl: method
// so you shouldn't release it again
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
[url release]; // don't do this!
Remember that you should only release objects that were created using alloc, new or retain . Objects returned from other methods are always in an auoreleased state (by convention).