I am facing an annoying problem. I have an application who is basicly made of several methods:
viewDidload, connection:didReceiveResponse, connection:didReceiveData...
In my viewDidload, I define a NSURLRequest to a personal websiten, and right after and before it I added a label.text=#"xxx". I know the problem doesn't come from linking the label in IB because it used to display what I wanted.
But now it seems none of those two label.text instructions are working even though I know my NSURLRequest works because the number of bytes received changes when I change the website... Why is that ? And I'm guessing the other instructions that come after aren't working either.
I will give more details when I can in case anyone can enlighten me on this.
Have a good day and thanks for your help
- (void)viewDidLoad {
[super viewDidLoad];
label.text=#"rrr";
request=[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://mywebsite.aspx?example=5"] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
label.text=#"aeza";
NSURLConnection *connection=[[NSURLConnection alloc] initWithRequest:request delegate:self];
if (connection) {
receiveddata=[[NSMutableData data] retain];
label.text=#"NO BUG";
}
else {
label.text=#"BUG";
}
datastring = [[NSString alloc] initWithData:receiveddata encoding:NSUTF8StringEncoding];
components=[datastring componentsSeparatedByString:#"|"];
label.text=datastring;
[datastring release];
}
-(void) connection:(NSURLConnection *)connection didReceiveResponse: (NSURLResponse *)response
{
[receiveddata setLength:0];
}
-(void) connection: (NSURLConnection *)connection didReceiveData: (NSData *)data
{
[receiveddata appendData:data];
}
-(void)connection: (NSURLConnection *)connection didFailWithError:(NSError *)error
{
[connection release];
[receiveddata release];
NSLog(#"Connection failed! Error - %# %#",
[error localizedDescription],
[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
}
-(void) connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"Succeeded! Received %d bytes of data",[receiveddata length]);
[connection release];
[receiveddata release];
}
#end
I would move that setup logic to -viewWillAppear, rather than the -viewDidLoad.
Nevermind, I got this to work by moving instructions to another method.
Related
my app is using network connections as in here:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
.
.
.
receivedData = [[NSMutableData alloc] init];
}
.
-(void) dataFromWeb{
request = [[NSURLRequest alloc] initWithURL: url];
theConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if (theConnection) {
receivedData = [[NSMutableData data] retain];
NSLog(#"** NSURL request sent !");
} else {
NSLog(#"Connection failed");
}
.
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
.
.
.
[theConnection release];
[request release];
// Here - using receivedData
[receivedData release];
}
.
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[receivedData setLength:0];
}
.
- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error
{
[theConnection release];
[request release];
[receivedData release];
}
.
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[receivedData appendData:data];
}
. Down the road in the app is this snippet (onButtonPressed):
if (theConnection) {
// Create the NSMutableData to hold the received data.
// receivedData is an instance variable declared elsewhere.
receivedData = [[NSMutableData data] retain];
} else {
NSLog(#"Connection failed");
}
What I want to understand is:
If I wanted to create another simultaneous URLRequest, would I need to use a different connection so that data arriving from the web won't mix up upon retrieval?
In this code, what happens is that sometimes the code would crash on the line of the function didReceiveResponse() setLength:0, when the app crashed I saw receivedData=nil should I change the line to
if(receivedData!=nil)
[receivedData setLength:0];
else
receivedData = [[NSMutableData data] retain];
I am not so sure what this line is doing receivedData = [[NSMutableData data] retain];
I think that the easiest way to handle 2 connections is duplicating the NSMutableData. I use in that way, and it works perfectly for me.
Fist you do this in the point you want the second connection:
receivedData2 = [[NSMutableData alloc] init];
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
if (receivedData2) {
[receivedData2 setLength:0];
}
else
{
[receivedData setLength:0];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
if (receivedData2) {
[receivedData2 appendData:data];
}
else {
[receivedData appendData:data];
}
}
And then, in the method you ask for receivedData2 or receivedData
And when you use it don´t forget to do:
receivedData2=nil;
[receivedData2 setLength:0];
You would of course need a different NSURLConnection.
You can do that, but better find out, why it is nil, because it shouldn't.
It allocates an empty NSMutableData object. But this seems like ugly code for me, because you create an autoreleased object and retain it. Better write [NSMutableData new] or [[NSMutableData alloc] init].
I have this code that is trying to access an FTP using the URL. But I cant get access because it's username and password protected. How do I implement that so I can get into the server? Here is my code:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[receivedData appendData:data];
}
- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error
{
NSLog(#"Connection failed! Error - %# %#",
[error localizedDescription],
[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// do something with the data
// receivedData is declared as a method instance elsewhere
NSLog(#"Succeeded! Received %d bytes of data",[receivedData length]);
// release the connection, and the data object
//[connection release];
//[receivedData release];
}
-(IBAction)getURL:(id)sender {
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:#"ftp://10.0.1.***/App_Data/text.txt"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
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];
} else {
// Inform the user that the connection failed.
NSLog(#"connection failed");
}}
The user id and password can be embedded in the URL:
ftp://userid:password#10.0.1.***/App_Data/text.txt
I write iPhone application. In this app, I use Twitter framework. In this framework, call back function made in desynchronization is in other thread.
In my view controller,
ViewController.m
[accountStore requestAccessToAccountsWithType:accountType
withCompletionHandler:^(BOOL granted, NSError *error) {
if (granted) {
if (account == nil) {
NSArray *accountArray = [accountStore accountsWithAccountType:accountType];
account = [accountArray objectAtIndex:2];
}
if (account != nil){
NSURL *url = [NSURL URLWithString:#"http://api.twitter.com/1/statuses/user_timeline.json"];
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
[params setObject:#"1" forKey:#"count"];
TWRequest *request = [[TWRequest alloc] initWithURL:url
parameters:params
requestMethod:TWRequestMethodGET];
[request setAccount:account];
[request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
if (responseData) {
//Throw response data to other Web API
[self otherAPI:responseData];
[[NSRunLoop currentRunLoop] run];
}
}];
}
}
}];
And I write these method in this class.
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
- (void)connectionDidFinishLoading:(NSURLConnection *)connection;
But I cannot receive full data from other API. I can receive only first data. I think there are some problems in conducting multi thread.
Therefore I'd like to let me know what's wrong in this code.
I think I see your problem. -connection:didReceiveData: is called multiple times, you need build up a NSMutableData object which will contain the whole message.
Note: This only works for a single download per instance at one time.
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
self.responseData = [[NSMutableData dataWithCapacity:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.responseData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// self.responseData has all the data.
}
A short explanation what I want to do: I'm using NSURLConnection to connect to a SSL webpage which is my API. The servers certificate is a self signed one so you have to accept it, for example in a web browser. I've found a solution on Stack Overflow how to do the trick (How to use NSURLConnection to connect with SSL for an untrusted cert?)
So I've added the NSURLConnection delegate to use methods like "didReceiveAuthenticationChallenge". As a result of that I cannot use this:
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&error];
because there is no possibility to use the delegate functions in this case. My question is the following: I need a function which looks like this:
- (NSDictionary *)getData : (NSArray *)parameter {
[...|
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[...]
return myDictionary;
}
how can I return a NSDictionary by using this? As far as you know the delegate function of NSURLConnection are called now and the response isn't available at this point. The problem is that the view controller depends on this response so I need to return the dictionary directly... Does anybody know a solution for this? What about a callback function?
okay, I've found a solution for that. A very good thing is to use blocks in objective-c.
First of all you have to add some methods to NSURLRequest and NSURL:
#implementation NSURLRequest (URLFetcher)
- (void)fetchDataWithResponseBlock:(void (^)(FetchResponse *response))block {
FetchResponse *response = [[FetchResponse alloc] initWithBlock:block];
[[NSURLConnection connectionWithRequest:self delegate:response] start];
[response release];
}
#end
#implementation NSURL (URLFetcher)
- (void)fetchDataWithResponseBlock:(void (^)(FetchResponse *response))block {
[[NSURLRequest requestWithURL:self] fetchDataWithResponseBlock:block];
}
#end
And than just implement the follwing class:
#implementation FetchResponse
- (id)initWithBlock:(void(^)(FetchResponse *response))block {
if ((self = [super init])) {
_block = [block copy];
}
return self;
}
- (NSData *)data {
return _data;
}
- (NSURLResponse *)response {
return _response;
}
- (NSError *)error {
return _error;
}
- (NSInteger)statusCode {
if ([_response isKindOfClass:[NSHTTPURLResponse class]]) return [(NSHTTPURLResponse *)_response statusCode];
return 0;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
_response = response;
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
if (!_data) _data = [[NSMutableData alloc] init];
[_data appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
_block(self);
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
_error = error;
_block(self);
}
Now you can do the follwing, some kind of callback function:
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"http://..."];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setValue:#"application/json" forHTTPHeaderField:#"accept"];
[request fetchDataWithResponseBlock:^(FetchResponse *response) {
if (response.error || response.statusCode != 200)
NSLog(#"Error: %#", response.error);
else {
//use response.data
}
}];
Here you can find the orginal german solution by ICNH: Asynchrones I/O mit Bloecken
Thank you very much for this!
My suggestion would be to use some other delegate methods for NSURLConnection like connection:didReceiveResponse: or connection:didReceiveData:. You should probably keep a use a set up like so:
#interface MyClass : NSObject {
…
NSMutableData *responseData;
}
…
#end
- (void)startConnection {
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:request delegate:self];
if (theConnection) {
responseData = [[NSMutableData data] retain];
} else {
// connection failed
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
// connection could have been redirected, reset the data
[responseData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[responseData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// connection is done, do what you want
………
// don't leak the connection or the response when you are done with them
[connection release];
[responseData release];
}
// for your authentication challenge
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace (NSURLProtectionSpace *)protectionSpace {
return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
if ([trustedHosts containsObject:challenge.protectionSpace.host])
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}
As a disclaimer I'd like to state that I'm fairly new to Objective-C and Cocoa. Currently I'm trying to write a basic application that can POST XML data to a particular endpoint. To achieve this, I've created a ServiceRouter class which uses NSURLConnection to post XML data to a particular URL.
The ServiceRouter class is intended as a base for subclasses which contain webservice-specific XML queries. In the example below, I subclass ServiceRouter to create a ServiceImplementation class.
When it's time to generate and POST the XML, I create an instance of the ServiceImplementation class like so:
[[ServiceImplementation alloc] createServiceSpecificXML];
This all seems to work fine. The issue is that Leaks reports a number of issues. Being fairly inexperienced, I'm not really sure where to start. For the most part, the NSURLConnection code is lifted from Apple's documentation.
Following basic memory management rules, I imagine I will have to release my ServiceImplementation instance at some point. What I'm confused about is how this should be handled given the asynchronous nature of NSURLConnection. Is this a candidate for autorelease?
I'm hoping that someone with more Objective-C/Cocoa experience can look things over and tell me if I'm moving in the right direction.
Here's the ServiceRouter class:
#interface ServiceRouter : NSObject {
NSMutableData *receivedData;
}
-(void)postXMLToURL:(NSString *)url xml:(NSString *)xmlData;
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
-(void)connectionDidFinishingLoading:(NSURLConnection *)connection;
#end
#implementation ServiceRouter
- (void)postXMLToURL:(NSString *)url xml:(NSString *)xmlData
{
NSLog(#"Posting XML to URL: %#", url);
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url]];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/xml" forHTTPHeaderField:#"Content-type"];
[request setValue:#"application/xml" forHTTPHeaderField:#"Accept"];
[request setValue:[NSString stringWithFormat:#"%d", [xmlData length]] forHTTPHeaderField:#"Content-length"];
[request setHTTPBody:[xmlData dataUsingEncoding: NSUTF8StringEncoding]];
NSURLConnection *connection = [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
if(connection) {
NSLog(#"Connection created");
receivedData = [[NSMutableData data] retain];
} else {
NSLog(#"Issue with connection!");
}
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
if([response respondsToSelector:#selector(statusCode)])
{
int statusCode = [((NSHTTPURLResponse *)response) statusCode];
NSLog(#"HTTP Response code: %i", statusCode);
}
NSLog(#"Received response");
[receivedData setLength:0];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSLog(#"Received data: %#", data);
[receivedData appendData:data];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
[connection release];
[receivedData release];
NSLog(#"Connection failed! Error - %#", [error localizedDescription]);
}
-(void)connectionDidFinishingLoading:(NSURLConnection *)connection
{
NSLog(#"Succeeded! Received %d bytes of data", [receivedData length]);
[connection release];
[receivedData release];
}
#end
Here's my ServiceImplementation class:
#interface ServiceImplementation : ServiceRouter {
}
-(void) createServiceSpecificXML;
#end
#implementation ServiceImplementation
-(void) createServiceSpecificXML
{
NSString *xmlData = #"<example><ignore/></example>";
[super postXMLToURL:#"http://site.com/endpoint.xml" xml:xmlData];
}
#end
You never initialize the instance. Merely allocing is not sufficient. You must call init or some other initializer — preferably on the same line as alloc.
From your (working but odd) construct of [[NSMutableData data] retain], I'm going to guess you haven't read a lot of Apple's basic primers. I would recommend at least The Objective-C Programming Language and the memory management guide. Neither is very long, and between these two, I think you'll clear up a lot of your uncertainties.