NSURLConnection delegate methods are called in Simulator but not in Device - iphone

I created a NSObject, which downloads some data from a php web service. The problem is that when i run the app in the simulator works well, but in the device the delegate methods are not called. I donĀ“t know why. I write all the delegates methods but none of them are called. This is a part of the NSObject code:
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:hostStr]];
NSLog(#"%# URL Engine",hostStr);
dispatch_async(dispatch_get_main_queue(), ^{
_connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
// while(!finished) {
// [[NSRunLoop currentRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate distantFuture]];
//}
[_connection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
[_connection start];
});

It's tricky about call dispatch_async to get the connection run on the main thread queue.
Try this if you want to let your delegates get called on the main thread:
[self performSelectorOnMainThread:#selector(startConnection)
withObject:Nil
waitUntilDone:NO];
If you want to let your delegates get called on the other thread, try:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop];
[connection scheduleInRunLoop:currentRunLoop forMode:NSRunLoopCommonModes];
[connection start];
[currentRunLoop run];
});

Related

How do I start an Asynchronous NSURLConnection inside an NSOperation?

I want to do an Asynchrous NSURLConnection inside of an NSOperation on a background thread.
it is because I'm doing some very expensive operations on the data as they come back.
This is a very similar question to what they asked here:
How do I do an Asynchronous NSURLConnection inside an NSOperation?
but the difference is that I run the connection in another class.
Here is my first attempt:
In my MainViewController:
#property (nonatomic, strong) NSOperationQueue *requestQueue;
#pragma mark - Lazy initialization
- (NSOperationQueue *)requestQueue
{
if (!_requestQueue) {
_requestQueue = [[NSOperationQueue alloc] init];
_requestQueue.name = #"Request Start Application Queue";
_requestQueue.maxConcurrentOperationCount = 1;
}
return _requestQueue;
}
-(void)callToServer
{
URLJsonRequest *request = [URLRequestFactory createRequest:REQUEST_INTERFACE_CLIENT_VERSION
delegate:self];
RequestSender *requestSender = [[RequestSender alloc]initWithPhotoRecord:request delegate:self];
[self.requestQueue addOperation:requestSender];
}
Here is my operation:
- (id)initWithPhotoRecord:(URLJsonRequest *)request
delegate:(id<RequestSenderDelegate>) theDelegate{
if (self = [super init])
{
self.delegate = theDelegate;
self.jsonRequest = request;
}
return self;
}
- (void)main {
//Apple recommends using #autoreleasepool block instead of alloc and init NSAutoreleasePool, because blocks are more efficient. You might use NSAuoreleasePool instead and that would be fine.
#autoreleasepool
{
if (self.isCancelled)
return;
[self.jsonRequest start];
}
}
Here is my Request start function:
-(void) start
{
NSURL *url = [NSURL URLWithString:#"http://google.com"];
NSURLRequest *theRequest = [NSURLRequest requestWithURL:url];
urlConnection = [[[NSURLConnection alloc] initWithRequest:theRequest delegate:self]autorelease];
[urlConnection start];
[theRequest release]
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
NSLog(#"Received reponse from connection");
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
}
I do not get a response from the server.
A couple of approaches:
Schedule the NSURLConnection in the main run loop, by using the startImmediately parameter of NO, set the run loop, and only then should you start the connection, e.g.:
urlConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self startImmediately:NO];
[urlConnection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
[urlConnection start];
Create a dedicated thread for the the connection and schedule the connection in the run loop you create for that thread. See AFURLConnectionOperation.m in AFNetworking source for an example of this.
Actually use AFNetworking, which gives you NSOperation based operations that you can add to your queue, and takes care of this run loop stuff for you.
So, AFNetworking does something like:
+ (void)networkRequestThreadEntryPoint:(id)__unused object {
#autoreleasepool {
[[NSThread currentThread] setName:#"NetworkingThread"];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[runLoop run];
}
}
+ (NSThread *)networkRequestThread {
static NSThread *_networkRequestThread = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_networkRequestThread = [[NSThread alloc] initWithTarget:self
selector:#selector(networkRequestThreadEntryPoint:)
object:nil];
[_networkRequestThread start];
});
return _networkRequestThread;
}
So I do something like the following. First I have a few private properties:
#property (nonatomic, readwrite, getter = isExecuting) BOOL executing;
#property (nonatomic, readwrite, getter = isFinished) BOOL finished;
#property (nonatomic, weak) NSURLConnection *connection;
Then the network operation can then do something like:
#synthesize executing = _executing;
#synthesize finished = _finished;
- (instancetype)init {
self = [super init];
if (self) {
_executing = NO;
_finished = NO;
}
return self;
}
- (void)start {
if (self.isCancelled) {
[self completeOperation];
return;
}
self.executing = YES;
[self performSelector:#selector(startInNetworkRequestThread)
onThread:[[self class] networkRequestThread]
withObject:nil
waitUntilDone:NO];
}
- (void)startInNetworkRequestThread {
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:self.request
delegate:self
startImmediately:NO];
[connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[connection start];
self.connection = connection;
}
- (void)completeOperation {
self.executing = NO;
self.finished = YES;
}
- (void)setFinished:(BOOL)finished {
if (finished != _finished) {
[self willChangeValueForKey:#"isFinished"];
_finished = finished;
[self didChangeValueForKey:#"isFinished"];
}
}
- (void)setExecuting:(BOOL)executing {
if (executing != _executing) {
[self willChangeValueForKey:#"isExecuting"];
_executing = executing;
[self didChangeValueForKey:#"isExecuting"];
}
}
- (BOOL)isConcurrent {
return YES;
}
- (BOOL)isAsynchronous {
return YES;
}
// all of my NSURLConnectionDataDelegate stuff here, for example, upon completion:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// I call the appropriate completion blocks here, do cleanup, etc. and then, when done:
[self completeOperation];
}
-(void)start
{
[self willChangeValueForKey:#"isExecuting"];
_isExecuting = YES;
[self didChangeValueForKey:#"isExecuting"];
NSURL* url = [[NSURL alloc] initWithString:#"http://url.to/feed.xml"];
NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20];
_connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]; // ivar
[request release];
[url release];
// Here is the trick
NSPort* port = [NSPort port];
NSRunLoop* rl = [NSRunLoop currentRunLoop]; // Get the runloop
[rl addPort:port forMode:NSDefaultRunLoopMode];
[_connection scheduleInRunLoop:rl forMode:NSDefaultRunLoopMode];
[_connection start];
[rl run];
}
More details can be found here: link
I know this post is over a year old, but I wanted to add some suggestions for people who may run the same problem trying to create own async network operation.
You need to add runloop to operation that runs in background and you should stop it when the operations has finished.
There actually 2 simple options:
Option 1 - using NSRunLoop
NSPort *port = [NSPort port];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:port forMode:NSDefaultRunLoopMode];
[self.connection scheduleInRunLoop:runLoop forMode:NSDefaultRunLoopMode];
[self.connection start];
[runLoop run];
and you need to stop when the operation is finished:
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
NSDate *date = [NSDate distantFuture];
while (!runLoopIsStopped && [runLoop runMode:NSDefaultRunLoopMode beforeDate:date]);
Option 2 - using CF
You need to add
CFRunLoopRun();
when you start operation
and call
CFRunLoopStop(CFRunLoopGetCurrent());
when you finish operation.
Read the following post: CFRunLoopRun() vs [NSRunLoop run]

NSURLConnection Delegate methods are not getting called

I have had this problem and i went through so many posts here on stack overflow and everywhere else and couldnt find a solution. Im running it on the main thread as well. Code as follows.
#interface JsonViewController : UIViewController <UIActionSheetDelegate,UIWebViewDelegate,NSURLConnectionDelegate>
{
....
}
#implementation JsonViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://google.com"] cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:60];
rssFeedDetailViewConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
NSLog(#"PRINTING RSS FEED %#", rssFeedDetailViewConnection);
[rssFeedDetailViewConnection scheduleInRunLoop:[NSRunLoop mainRunLoop]
forMode:NSDefaultRunLoopMode];
[rssFeedDetailViewConnection start];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSLog(#"Hello");
responseData = [[NSMutableData alloc] init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSLog(#"Hello");
[responseData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(#"Hello");
[responseData release];
[connection release];
// Show error message
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"Hello");
// Use responseData
[responseData release];
[connection release];
}
Any help would be greatly appreciated. Ive been stuck with this for two days now..
You don't need these statements:
[rssFeedDetailViewConnection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
[rssFeedDetailViewConnection start];
since you're using initWithRequest:delegate:. This call already starts loading the data.
What happens if you remove these particular statements?
More info on the NSURLConnection.
Have a boolean called finished and add this code where ever you have written your NSURLConnection Code.
while(!finished) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
and in your connectionDidFinishLoading just add this
finished = TRUE;
and that works. Its not the best solution i understand but it works.

How to send request to server in background repeatedly?

In my application I need to send request to server to get xml after particular time interval say 1 hour to get the latest data.I want to perform this activity in background.Can anyone suggest how I can achieve this?
Thanks in advance!
Use NSTimer for repeatedly request and if u want to perform request in background thread u should do something like that:
backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler: ^{
[[UIApplication sharedApplication] endBackgroundTask:backgroundTask];
backgroundTask = UIBackgroundTaskInvalid;
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//start url request
});
//after url request complete
[[UIApplication sharedApplication] endBackgroundTask:backgroundTask];
backgroundTask = UIBackgroundTaskInvalid;
To solve above problem I created NSOperation to send request to server and parse response.Its very useful and better than using thread.
1.I created NSTimer instance which will call -(void)sendRequestToGetData:(NSTimer *)timer after particular time interval as follows:
//Initialize NSTimer to repeat the process after particular time interval...
NSTimer *timer = [NSTimer timerWithTimeInterval:60.0 target:self selector:#selector(sendRequestToGetData:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
2.Then inside sendRequestToGetData I created NSOperation by subclassing NSOperation as follows:
-(void)sendRequestToGetData:(NSTimer *)timer
{
//Check whether user is online or not...
if(!([[Reachability sharedReachability] internetConnectionStatus] == NotReachable))
{
NSURL *theURL = [NSURL URLWithString:myurl];
NSOperationQueue *operationQueue = [NSOperationQueue new];
DataDownloadOperation *operation = [[DataDownloadOperation alloc] initWithURL:theURL];
[operationQueue addOperation:operation];
[operation release];
}
}
Note: DataDownloadOperation is subclass of NSOperation.
//DataDownloadOperation.h
#import <Foundation/Foundation.h>
#interface DataDownloadOperation : NSOperation
{
NSURL *targetURL;
}
#property(retain) NSURL *targetURL;
- (id)initWithURL:(NSURL*)url;
#end
//DataDownloadOperation.m
#import "DataDownloadOperation.h"
#import "XMLParser.h"
#implementation DataDownloadOperation
#synthesize targetURL;
- (id)initWithURL:(NSURL*)url
{
if (![super init]) return nil;
self.targetURL = url;
return self;
}
- (void)dealloc {
[targetURL release], targetURL = nil;
[super dealloc];
}
- (void)main {
NSData *data = [NSData dataWithContentsOfURL:self.targetURL];
XMLParser *theXMLParser = [[XMLParser alloc]init];
NSError *theError = NULL;
[theXMLParser parseXMLFileWithData:data parseError:&theError];
NSLog(#"Parse data1111:%#",theXMLParser.mParsedDict);
[theXMLParser release];
}
#end

NSURLConnection delegate is not call when call from NSThread's function

I sent NSURLConnection request it is working fine. Now I want to refresh the information i.e. resend the NSURLConnection.Refresh is working when call from IBAction of button. But is not working from NSThread method. How to I solve this problem. Here NSThread function for running the system time. When the time is equal to 1:00 am I want to refresh the API. But it is not call the delegate of NSURLConnection.
This is NSURLConnection code:
-(void)displays:(model *)place
{
NSString *strs=[#"http://www.earthtools.org/timezone-1.1/" stringByAppendingString:[NSString stringWithFormat:#"%#/%#",place.latitude,place.longitude]];
NSMutableURLRequest *request=[NSMutableURLRequest requestWithURL:[NSURL URLWithString:strs]];
NSURLConnection *reqTimeZone=[NSURLConnection connectionWithRequest:request delegate:self];
[reqTimeZone start]; //here request not get start
}
Above code is with in function called "displays" argument is one instance of class it has all place details.
NSthread function code:
- (void) setTimer {
//assign current time
[self countDown];
}
- (void) countDown {
//count the current time
if(hrs==12&& meridian==#"pm")
[self display:(placedetails)];//it calls the displays function but NSURLConnection is not get start.
[NSThread detachNewThreadSelector:#selector(setTimer) toTarget:self withObject:nil];
}
Above display function is called placedetails assigned but NSURLConnection delegate is not called.
For the delegate methods to be called, you need to attach your thread's runloop to NSURLConnection. Since you are creating a thread and not attaching NSURLConnection to thread's RunLoop, connection delegate methods won't get fired.
Here is an example:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// I am creating a button and adding it to viewController's view
UIButton *bttn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[bttn setFrame:CGRectMake(100.0f, 200.0f, 120.0f, 50.0f)];
[bttn setTitle:#"Download" forState:UIControlStateNormal];
[bttn addTarget:self action:#selector(spawnThreadForDownload) forControlEvents:UIControlEventTouchUpInside];
[[self view] addSubview:bttn];
}
- (void)spawnThreadForDownload
{
[NSThread detachNewThreadSelector:#selector(downloadAndParse) toTarget:self withObject:nil];
}
- (void)downloadAndParse
{
#autoreleasepool {
NSURL *url = [NSURL URLWithString:#"http://apple.com"];
NSURLRequest *req = [NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
timeoutInterval:20.0f];
NSURLConnection *conn = [NSURLConnection connectionWithRequest:req delegate:self];
// Run the currentRunLoop of your thread (Every thread comes with its own RunLoop)
[[NSRunLoop currentRunLoop] run];
// Schedule your connection to run on threads runLoop.
[conn scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
}
// NSURLConnectionDelegate methods
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"Connection failed with error: %#",[error localizedDescription]);
}
// NSURLConnectionDataDelegate methods
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"Connection finished downloading");
}

NSStream on Background didn't call

- (void)applicationDidEnterBackground:(UIApplication *)application {
NSOutputStream *outputStream;
NSInputStream *inputStream;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSRunLoopCommonModes];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSRunLoopCommonModes];
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
});
}
Input,OutputStream delegate didn't call.What i am doing wrong.
Did you run the current run loop by following way ?
do {
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate date]];
} while (done == TRUE);
You seem to have omitted the streams' initialization, e.g.
NSInputStream *inputStream = [NSInputStream inputStreamWithURL: myURL];
NSOutputStream *outputStream = [NSOutputStream outputStreamToMemory];