Thread still running of old view - ios

I have an app where there are five screens.
On each screen, in viewDidLoad I am accessing data from server.
On each screen I have next button.
When I go from screen one to screen five (by clicking Next 4 times), in NSLog, I still see the process done by all previous four view controller.
Is there any way, how can I kill those threads?
In short, I don't want to do any process when I go away from that view i.e. if I go from view 3 to 4, I want to stop the task that I was for view 3.
Getting data of earlier views & waiting for that data (which is unwanted) is not good for app, hence I want like what I explained above.
Edit 1
Below is the code I use for reading the data.
.h
#property(nonatomic, retain) NSMutableData *webData;
#property(nonatomic, retain) NSMutableData *data;
Using below I request the data
.m
NSString *myTMainURL = [NSString stringWithFormat:#"http://www.google.com"];
NSURL *url = [NSURL URLWithString:myTMainURL];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
For reading, below is how I read.
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSLog(#"didReceiveResponse");
data = [[NSMutableData alloc] init ];
[webData setLength: 0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)theData {
NSLog(#"didReceiveData");
[data appendData:theData];
[webData appendData:data];
NSLog(#"didreceveidata leng===%d===%d", [webData length], [data length]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"connectionDidFinishLoading");
NSString *myDataFromLink = [[NSString alloc] initWithBytes: [data mutableBytes] length:[data length] encoding:NSUTF8StringEncoding];
NSLog(#"myDataFromLink====%#--", myDataFromLink);
}

In viewWillDisappear:, send cancel to whatever operation is running.
This, of course, assumes you have a cancelable task/method/operation.
For example, for network requests, if you use NSURLConnection this is the case when you employ the delegate approach. With NSURLConnection's convenient class method sendAsynchronousRequest:queue:completionHandler: this is not possible. Thus, any serious application would use the delegate approach, since a long running asynchronous operation must be cancelable.

You can use NSOperation and cancel the operation when you go to the next view may be in the action of the next button or just in viewWillDisappear: method
Edit
Since You are using NSURLConnection then you can call cancel on the connection in viewWillDisappear:
.h
#property(nonatomic, retain) NSMutableData *webData;
#property(nonatomic, retain) NSMutableData *data;
#property(nonatomic, retain) NSURLConnection *connection;
.m
NSString *myTMainURL = [NSString stringWithFormat:#"http://www.google.com"];
NSURL *url = [NSURL URLWithString:myTMainURL];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
in viewWillDisappear:
[self.connection cancel]

Since you are using NSURLConnection, You can cancel that connection and stop the NSURLRequest which you are using with your NSURLConnection with following code:
-(void)viewDidDisappear
{
[super viewDidDisappear];
[connection cancel]; // Here connection is your NSURLConnection object.
}

Related

How do I wait for a web call to return data on iOS without blocking the main thread?

When my iOS app starts, I need to get some critical settings from my server (example: http://www.example.com/critical_app_settings.php). I need to get this data before I load up the user's data.
What is the proper way to make this call, basically "pause" the app, and yet still not block the main thread and be in good compliance?
Currently I am doing this example, which obviously isn't correct, because it completely blocks everything:
NSData *myRequestData = [NSData dataWithBytes:[myRequestString UTF8String] length:[myRequestString length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL: [NSURL URLWithString: myURLString]];
[request setHTTPMethod: #"POST"];
[request setHTTPBody:myRequestData];
NSURLResponse *response;
NSError *error;
NSString *returnString = [[NSString alloc] init];
returnString = [[NSString alloc] initWithData:[NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error]
encoding: NSASCIIStringEncoding];
Instead of using NSURLConnection sendSynchronousRequest you can use NSURLSessionDataTask. Also sendSynchronousRequest is deprecated from iOS 9. Code will be as follows
NSData *myRequestData = [NSData dataWithBytes:[myRequestString UTF8String] length:[myRequestString length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL: [NSURL URLWithString: myURLString]];
[request setHTTPMethod: #"POST"];
[request setHTTPBody:myRequestData];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
if ([httpResponse statusCode]==200) {
dispatch_async(dispatch_get_main_queue(), ^{
NSString* returnString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
//do whatever operations necessary on main thread
});
}
else
{
dispatch_async(dispatch_get_main_queue(), ^{
//action on failure
});
}
}];
[dataTask resume];
Since it is async operation your main thread will not be blocked. As far as pausing app is concerned you can either show some kind of activity indicator or disable user interaction before making call. Once response comes hide indicator or enable the UI.
You should NEVER use sendSynchronousRequest. You should pretend that method does not exist. It's a very bad idea to call that on the main thread, and the system will likely kill your app if the remote server takes more than a few seconds to serve your request.
As Vishnu suggests, use NSURLSession instead.
If you want to "pause the app" while your content loads, display some sort of modal alert/view while the load takes place. One option is to use a UIAlertController with no actions added, and then call dismissViewController:animated: on it once the load is complete.
Another thing I've done is to display a view that completely covers the screen, filled in 50% opaque black, and put a view inside that with a progress indicator inside it, plus a please wait message (and any other cosmetic stuff you want.) The 50% opaque view both dims the rest of the screen and prevents the user from clicking on anything, and the progress indicator lets the user know that your app is working.
Both of these approaches work because your download is taking place asynchronously, so you can display an alert or a progress indicator and it animates as expected.
/**
You can use a protocol to handle that for you, for example you can create a class that connect to the URL you can use NSURLSession but it seems that you are familiar with NSURLConnection. So lets create your class that will connect and receive the information from your URL something like this:
The H file
*/
#import <Foundation/Foundation.h>
/* Here is the custome protocol to manage the response at will */
#protocol OnResponseDelegate;
/* Here the class is implementing the protocol to receive the callbaks from the NSURLConnection when the request is processing */
#interface WgetData : NSObject <NSURLConnectionDataDelegate>
#property (strong, nonatomic) id<OnResponseDelegate>handler;
#property (strong, nonatomic) NSMutableData *responseData;
#property (strong, nonatomic) NSURLConnection *connection;
#property (strong, nonatomic) NSMutableURLRequest *request;
-(id)initWithUrl:(NSString*)url postBody:(NSString*)body delegate:(id)delegate;
-(void)execute;
#end
/* here is the real definition of the protocol to manage the response */
#protocol OnResponseDelegate <NSObject>
-(void)onPreStart;
-(void)onResponse:(NSData*)response;
#end
/*
End H file
The code above is using a protocol to handle the data that you want to receive from the URL. lets do the implementation file it should look like this:
The M file
*/
#import "WgetData.h"
#define TIMEOUT 700
static NSString * const CONTENT_TYPE_VALUE = #"application/json";
static NSString * const CONTENT_TYPE_HEADER = #"Content-Type";
static NSString * const GET = #"GET";
static NSString * const POST = #"POST";
#implementation WgetData
#synthesize handler,responseData,connection,request;
-(id)initWithUrl:(NSString *)url postBody:(NSString *)body delegate:(id)delegate{
NSURL *requestUrl = [NSURL URLWithString:[url stringByAddingPercentEncodingWithAllowedCharacters: NSCharacterSet.URLQueryAllowedCharacterSet]];
[self setRequest:[NSMutableURLRequest requestWithURL:requestUrl cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:TIMEOUT]];
[self setResponseData:[NSMutableData new]];
[self setHandler:delegate];
[request setHTTPMethod:POST];
[request addValue:CONTENT_TYPE_VALUE forHTTPHeaderField:CONTENT_TYPE_HEADER];
[request setHTTPBody: [body dataUsingEncoding:NSUTF8StringEncoding]];
return self;
}
-(void)execute{
//here is the moment to prepare something in your main class before send the request to your URL
[handler onPreStart];
[self setConnection:[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES]];
}
/* this method belongs to the implementation of the NSURLConnectionDataDelegate to receive the data little by little */
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
[self.responseData appendData:data];
}
/* this method belongs to the implementation of the NSURLConnectyionDataDelegate that is runned only when the data received is complete */
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
/* if this method happen it means that the data is ready to delivery */
/* sending the information to the class that implements this class through the handler or delegate */
[handler onResponse:responseData];
}
#end
/*
End M file
Then you only need to implement this class with the next piece of code
For example in a ViewController inside a viewDidLoad
remember to call the protocol as a delegate to implement the methods in the right way
H file ViewController example
*/
#import <UIKit/UIKit.h>
#import "WgetData.h"
#interface ViewController : UIViewController <OnResponseDelegate>
#end
/*
End H ViewController file
M ViewController file
*/
#interface MainViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
WgetData *getData = [[WgetData alloc] initWithUrl:#"http://myurl.net" delegate:self];
[getData execute];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void) onPreStart{
// prepare something before run the request to your URL
}
- (void) onResponse:(NSData *)response{
// receive the data when the data is ready and convert o parse to String or JSON or Bytes or whatever
}
#end
/* End M file
So with all this example you can manage the right moment when you will receive the data to manage at your own will, and your main thread app can wait while your data will be received to be ready for use.
*/

UIImage returns null first time, until reloaded? [duplicate]

This question already has answers here:
didReceiveData is not getting all data
(2 answers)
Closed 7 years ago.
I retrieve NSData from a url, then I try set it to an image.
In the console it returns null, however when I request the data and set the image again, the image than loads?
Why do does this have to be done twice for it to load??
This is the two methods I use to get the picture.
-(void)getUserPicture {
//Grab and upload user profile picture
imageData = [[NSMutableData alloc] init]; // the image will be loaded in here
NSString *urlString = [NSString stringWithFormat:#"http://graph.facebook.com/%#/picture?type=large", userId];
NSMutableURLRequest *urlRequest =
[NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlString]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:3];
NSURLConnection *urlConnection = [[NSURLConnection alloc] initWithRequest:urlRequest
delegate:self];
if (!urlConnection) NSLog(#"Failed to download picture");
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
userPicture = [UIImage imageWithData:data];
NSLog(#"%#",userPicture); //returns null in console first time, until reloaded???
}
The connection:didReceiveData: method is called repeatedly as the data is loaded incrementally. You probably want the connectionDidFinishLoading: method instead.
By the way, you'll still likely need a connection:didReceiveData:, but rather than trying to create the image, you'll just be appending the new data to a buffer. Then in the connectionDidFinishLoading: method, you take the buffer and create your image.
Example code, but please add error handling as needed:
#property (strong, nonatomic) NSMutableData *data;
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
self.data = [NSMutableData data];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.data appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
self.userPicture = [UIImage imageWithData:self.data];
}

NSURLConnection sendAsynchronousRequest: How to check finished status code?

So I have some code like so:
#interface RequestHandler()
#property (nonatomic) NSInteger statusCode;
#end
#implementation RequestHandler
- (bool)sendRequest:(NSString *)surveyorId withData:(NSData *)requestData
{
[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:true];
if (self.statusCode == 200)
{
return YES;
}
return NO;
}
Clearly the routine will carry on into the if-else statement before the request has finished. Therefore, self.statusCode is not set properly in the delegate didReceiveResponse before it is checked. What would be the best way of doing this?
I am just thinking of adding another bool property that will be set in connectionDidFinishLoading and then loop until this property is set. Once it has done that, then it will check self.statusCode. However I am thinking this will block the thread will it not? It will be no different from a sendSynchronousRequest right? Is there any way to do this without putting it into a background thread?
Instead of your sendRequest:withData: method returning a BOOL indicating success/failure, it would be better for your RequestHandler to have a delegate. It could then let its delegate know about the success/failure/whatever else when the asynchronous request has finished, instead of trying to return this information from the sendRequest:withData: method (which, as you've found out, doesn't work so well).
So, you could define you delegate protocol something like this (just as an example - you might want to include some more information in these):
#protocol RequestHandlerDelegate <NSObject>
- (void)requestHandlerSuccessfullyCompletedRequest:(RequestHandler *)sender;
- (void)requestHandlerFailedToCompletedRequest:(RequestHandler *)sender;
#end
Then, give your RequestHandler a delegate property of something that conforms to this protocol:
#property (nonatomic, weak) id<RequestHandlerDelegate> delegate;
(Make sure you set something as the delegate!)
Then, when your asynchronous request completes, you can send your delegate the appropriate message, e.g.:
[self.delegate requestHandlerSuccessfullyCompletedRequest:self];
You'll need to implement the NSURLConnection delegate methods in RequestHandler (from your code, I assume you've already done that), or, if your are targeting iOS 7+, you could take a look at NSURLSession instead.
You have to implement 2 delegate methods:
Status code: - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
Received data: - (void)connection:(NSURLConnection *)connection
didReceiveData:(NSData *)data
Example usage:
Declaration
#interface RequestHandler : NSObject <NSURLConnectionDelegate>
{
NSMutableData *receivedData;
}
Request
- (void)sendRequest:(NSString *)surveyorId withData:(NSData *)requestData
{
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
// Apply params in http body
if (requestData) {
[request setHTTPBody:requestData];
}
[request setURL:url];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[connection start];
}
Delegates
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSHTTPURLResponse *responseCode = (NSHTTPURLResponse *)response;
if ([self.delegate respondsToSelector:#selector(didReceiveResponseCode:)]) {
[self.delegate didReceiveResponseCode:responseCode];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
receivedData = [[NSMutableData alloc] initWithData:data];
if ([self.delegate respondsToSelector:#selector(connectionSucceedWithData:)]) {
[self.delegate connectionSucceedWithData:receivedData];
}
}
Instead of using NSURLConnection with delegate methods you can use NSURLConnection sendAsynchronousRequest block in your code. In the example you can check connection error and compare status codes.
NSURL *URL = [NSURL URLWithString:#"http://yourURLHere.com"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:URL];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *rspreportStatus, NSData *datareportStatus, NSError *e)
{
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)rspreportStatus;
int code = [httpResponse statusCode];
if (e == nil && code == 200)
{
// SUCCESS
} else {
// NOT SUCCESS
}
}];
You can also check by logging this returnString.
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSString *returnString = [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding];
NSArray *arrpicResult = [returnString JSONValue];

delegate and declare variable receivedData in NSURLConnection

I want to create a NSURLConnection delegate in Xcode 4.5.2 for iOS because the documentation suggests it. For now I am putting the following code (taken directly from the documentation) into my AppDelegate.m in the method application didFinishLaunchingWithOptions:.
// Create the request.
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.apple.com/"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
// create the connection with the request
// and start loading the data
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
// Create the NSMutableData to hold the received data.
// receivedData is an instance variable declared elsewhere.
receivedData = [[NSMutableData data] retain];
} else {
// Inform the user that the connection failed.
}
How do I create the delegate in AppDelegate.h ?
Where and how do I declare the variable receivedData?
You shouldn't be doing this in the AppDelegate, but just to make it work, here's what you need to do.
1) In your AppDelegate.h, replace the interface declaration with this ::
#interface AppDelegate : UIResponder <UIApplicationDelegate, NSURLConnectionDataDelegate> {
NSMutableData *receivedData;
}
2) In your AppDelegate.m, add this method ::
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[receivedData setData:data];
NSLog(#"receivedData : %#", receivedData);
}

NSMutableData disappearing

in my Program, I have a NSMutableData variable that collect the information from http://www.nhara.org/scored_races-2013.htm. After about the third time it gets information from a website, when it contains 90810 bytes, it either disappears or becomes null because if I print it a NSString, it is null. Here is the code
- (void)viewWillAppear:(BOOL)animated
{
// Create a new data container for the stuff that comes back from the service
xmlData = [[NSMutableData alloc] initWithCapacity:180000];
[self fetchEntries];
[super viewWillAppear:animated];
}
- (void)fetchEntries
{
// Construct a URL that will ask the service for what you want
NSURL *url = [NSURL URLWithString: #"http://www.nhara.org/scored_races-2013.htm"];//
// Put that URL into an NSURLRequest
NSURLRequest *req = [NSURLRequest requestWithURL:url];
// Create a connection that will exchange this request for data from the URL
connection = [[NSURLConnection alloc] initWithRequest:req delegate:self startImmediately:YES];
}
- (void)connection:(NSURLConnection *)conn didReceiveData:(NSData *)data
{
// Add the incoming chunk of data to the container we are keeping
// The data always comes in the correct order
[xmlData appendData:data];
NSLog(#"%#",xmlData);
NSString *xmlCheck = [[[NSString alloc] initWithData:xmlData encoding:NSUTF8StringEncoding]autorelease];
NSLog(#"xmlCheck = %#", xmlCheck);
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"error= %#",error);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)conn {
// We are just checking to make sure we are getting the XML
NSString *xmlCheck = [[[NSString alloc] initWithData:xmlData encoding:NSUTF8StringEncoding] autorelease];
NSLog(#"xmlCheck2 = %#", xmlCheck);
}
What confuses me the most is that my NSMutableData stores data, but then loses it while claiming to have the same number of bytes.
Is there a constraint to the NSMutableData's size or is my problem just memory management?
You need to create a property for your xmlData variable. In your header file after your
#interface MyClass, make one like so
#property (nonatomic, retain) NSMutableData * xmlData;
If you are using ARC you leave it as strong if you using below ARC you change strong to retain. When you want to use your variable you do self.xmlData

Resources