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);
}
Related
I've tried most of advises from stackoverflow but none of them was good for me.
My client wants to save user data in json with GET request in format:
http://website.com/users/save.php?save={"email":"user#domen.com","name":"Will Smith"}
I'm using objective-c
Please advise
Thanks
Edit
my php file looks like this:
<?php
$save = $_GET['save'];
$fp = fopen("save_users_ww.txt", "a+");
fputs($fp,$save."\r\n");
fclose($fp);
?>
so post don't work for me. Thank you for help again
In case someone else will need a solution, this is my code:
.h file:
#interface WelcomeViewController : UIViewController <NSURLConnectionDelegate>
{
NSMutableData *_responseData;
}
.m file:
-(void)postToDataBaseWithName:(NSString*)nameToPost andEmail:(NSString*)emailToPost {
NSString *str = [NSString stringWithFormat:#"http://website.com/users/save.php?save={\"email\":\"%#\",\"name\":\"%#\"}", emailToPost, nameToPost];
str = [str stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString:str];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// Create url connection and fire request
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if(conn) {
}
}
also add delegate methods to see how it works:
#pragma mark NSURLConnection Delegate Methods
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
// A response has been received, this is where we initialize the instance var you created
// so that we can append data to it in the didReceiveData method
// Furthermore, this method is called each time there is a redirect so reinitializing it
// also serves to clear it
NSLog(#"didReceiveResponse");
_responseData = [[NSMutableData alloc] init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// Append the new data to the instance variable you declared
NSLog(#"didReceiveData");
[_responseData appendData:data];
}
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
willCacheResponse:(NSCachedURLResponse*)cachedResponse {
// Return nil to indicate not necessary to store a cached response for this connection
return nil;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// The request is complete and data has been received
// You can parse the stuff in your instance variable now
NSLog(#"connectionDidFinishLoading");
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
// The request has failed for some reason!
// Check the error var
NSLog(#"didFailWithError = %#", error);
}
I am developing a framework which requires error callbacks. I have used delegate methods to give error callbacks to the users(developer) of the framework. Can I use NSError to indicate error details in the delegate method or simply use NSDictionary instead ? Are there any memory issues concerned here?
Instead of delegate methods, you should use blocks with proper error handling, something like this:
Interface:
typedef void(^ResultBlock)(id result, NSError *error);
extern NSString * const DummyErrorDomain;
enum{
DummyErrorReason1,
DummyErrorReason2
};
#interface Dummy : NSObject
-(void)doSomething:(ResultBlock)resultBlock;
#end
Implementation with async NSURLConnection:
NSString * const DummyErrorDomain = #"DummyErrorDomain";
#interface Dummy ()
#property (strong) ResultBlock resultBlock;
#property NSURLConnection *connection;
#property NSMutableData *responseData;
#end
#implementation Dummy
-(void)doSomething:(ResultBlock)resultBlock
{
self.resultBlock = resultBlock;
self.responseData = [[NSMutableData alloc] init];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:#"dummyURL"]];
self.connection = [[NSURLConnection alloc]initWithRequest:request delegate:self];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
self.resultBlock(nil,error);
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
self.resultBlock(self.responseData, nil);
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
[self.responseData appendData:data];
}
#end
Usage:
-(void)usage
{
Dummy *dummy = [[Dummy alloc] init];
[dummy doSomething:^(id result, NSError *error) {
if(result){
// everything went fine
}else{
// error handling
}
}];
}
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];
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.
}
I am just new in IOS development. I've been trying to figure apple documentations. So I read this page:
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/URLLoadingSystem/Tasks/UsingNSURLConnection.html#//apple_ref/doc/uid/20001836-BAJEAIEE
and this is what I have done:
NSMutableData *testFileType;
// Create the request.
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:urlString]
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.
testFileType = [[NSMutableData data] retain];
NSLog(#"the connection is successful");
} else {
// Inform the user that the connection failed.
NSLog(#"the connection is unsuccessful");
}
[testFileType setLength:0];
[testFileType appendData:[NSMutableData data]];
Can anyone tell me what am I missing here?
Just creating the NSURLConnection is not enough. You also need to implement the didReceiveResponse and didFinishLoading delegate methods. Without these the connection downloads the file, but you never get to see it.
NSURLConnection sends a didReceiveResponse for every redirection when the headers are received. Then it sends a didReceiveData with some bytes of the file. Those you need to append to your mutable data. Finally you get a didFinishLoading where you know that you have gotten all data. In case of error you get a didFailWithError instead.
Look at the NSURLConnectionDelegate protocol documentation: https://developer.apple.com/library/mac/ipad/#documentation/Foundation/Reference/NSURLConnectionDelegate_Protocol/Reference/Reference.html
you should implement the following delegate methods:
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"Error: %d %#", [error code], [error localizedDescription]);
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
responseData = [NSMutableData data];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[responseData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[responseData writeToFile:savePath atomically:YES];
}
here responseData and savePath are instance variables declared with:
NSMutableData *responseData;
NSString *savePath;
and your class must conforms the NSURLConnectionDataDelegate and NSURLConnectionDelegate protocols.
For the code to work you probably want to set savePath to a working path like this
NSString *savePath = [NSTemporaryDirectory() stringByAppendingPathComponent:#"testfile.txt"];
after the download have finished, you can do anything to the file at savePath as you wish.