I'm trying to get XML from a page, but the NSURLConnection is not returning anything.
- (void)downloadDataWithMission:(NSString *)mission
{
// Create a new data container for the stuff that comes back from the service
xmlData = [[NSMutableData alloc] init];
// Construct a URL that will ask the service for what you want
NSString *urlstring = [NSString stringWithFormat:#"http://www.google.com/"];
// , mission, [self getCountry]
NSURL *url = [NSURL URLWithString:urlstring];
// Put that URL into an NSURLRequest
NSURLRequest *req = [NSURLRequest requestWithURL:url];
// Create a connection that will exchange this request for data from the URL
urlConnection = [[NSURLConnection alloc] initWithRequest:req delegate:self startImmediately:YES];
}
# pragma mark NSURLConnection
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
// This method is called when the server has determined that it
// has enough information to create the NSURLResponse.
// It can be called multiple times, for example in the case of a
// redirect, so each time we reset the data.
// receivedData is an instance variable declared elsewhere
[xmlData setLength:0];
}
// This method will be called several times as the data arrives
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// Add the incoming chunk of data to the container we are keeping
// The data always come in the correct order
[xmlData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// We are just checking to make sure we are getting the XML
NSString *xmlCheck = [[NSString alloc] initWithData:xmlData encoding:NSUTF8StringEncoding];
NSLog(#"xmlCheck = %#", xmlCheck);
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
// Release the connection object, we're done with it
urlConnection = nil;
// Release the xmlData object, we're done with it
xmlData = nil;
// Grab the description of the error object passed to us
NSString *errorString = [NSString stringWithFormat:#"Connection Failed: %#", [error localizedDescription]];
// Create and show an alreat view with this error displayed
UIAlertView *av = [[UIAlertView alloc] initWithTitle:#"Error" message:errorString delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[av show];
}
#end
Why is the connection not working? Is this a problem in the delegate? In another project, all worked fine. The base SDK was the same in those projects - iOS 6.1.
Everything up to this line works perfectly:
NSString *xmlCheck = [[NSString alloc] initWithData:xmlData encoding:NSUTF8StringEncoding];
However it does not handle the encoding I think. Maybe there is sone invalid UTF-8 char at google. Try NSASCIIStringEncoding instead and it will work. If you want to use UTF-8 you might need to dig into why google is not UTF-8 compliant.
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 using the Yelp Search API to basically just get a list of businesses for a search query.
It is pretty much a NSURLConnection is OAuth, but here is the code to initialize the request:
NSURL *URL = [NSURL URLWithString:appDelegate.yelpAdvancedURLString];
OAConsumer *consumer = [[OAConsumer alloc] initWithKey:#"this-is-my-key" secret:#"this-is-my-secret"];
OAToken *token = [[OAToken alloc] initWithKey:#"this-is-my-key" secret:#"this-is-my-secret"];
id<OASignatureProviding, NSObject> provider = [[OAHMAC_SHA1SignatureProvider alloc] init];
NSString *realm = nil;
OAMutableURLRequest *request = [[OAMutableURLRequest alloc] initWithURL:URL
consumer:consumer
token:token
realm:realm
signatureProvider:provider];
[request prepare];
responseData = [[NSMutableData alloc] init];
yelpConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
Then here:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[responseData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[responseData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(#"Error: %#, %#", [error localizedDescription], [error localizedFailureReason]);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle: #"Oops." message: #"Something screwed up. Please search again." delegate: nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
[MBProgressHUD hideHUDForView:self.view animated:YES];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if (connection == self.yelpConnection) {
[self setYelpString];
}
}
When I run this on iPhone, everything is working fine. However, when I run on iPad, the connection gets timed out. The following is from this line
NSLog(#"Error: %#, %#", [error localizedDescription], [error localizedFailureReason]);
Error: The request timed out., (null)
Also if I use a synchronous request, it seems to work using this:
NSData* result = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSDictionary* JSON = [NSJSONSerialization
JSONObjectWithData:result
options:kNilOptions
error:&error];
However, I want to avoid using synchronous as it freezes the app.
Is this Yelp API specific? Or am I just doing something wrong? Thanks in advance, I would appreciate any help.
If it helps, it times out approximately 10 seconds after sending the request.
create this type of NSMutableURLRequest :
NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:240.0];
I think the best approach is to change the init method in http://oauth.googlecode.com from
- (id)initWithURL:(NSURL *)aUrl
consumer:(OAConsumer *)aConsumer
token:(OAToken *)aToken
realm:(NSString *)aRealm
signatureProvider:(id<OASignatureProviding, NSObject>)aProvider
{
if (self = [super initWithURL:aUrl
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:10.0])
{
...
}
}
to
- (id)initWithURL:(NSURL *)aUrl
cachePolicy:(NSURLRequestCachePolicy)cachePolicy
timeoutInterval:(NSTimeInterval)timeoutInterval
consumer:(OAConsumer *)aConsumer
token:(OAToken *)aToken
realm:(NSString *)aRealm
signatureProvider:(id<OASignatureProviding, NSObject>)aProvider
{
if (self = [super initWithURL:aUrl
cachePolicy:cachePolicy
timeoutInterval:timeoutInterval])
{
...
}
and then check again, whether the timeout value which you specify will be honored by the connection.
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.
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
In the code below, I am looking for a way to limit the length of connected string. Let's say I only want to retrieve the first 100 characters. But I do not want to do the processing connected after retrieving. Is there a way to initialize NSString with certain length?
NSError* error = nil;
NSString *connected = [NSString stringWithContentsOfURL:[NSURL URLWithString:#"http://www.somesite.com"] encoding:NSASCIIStringEncoding error:&error];
You're going to have to retrieve the data yourself instead of using NSString's convenience method to do it. If you use, say, NSURLConnection or ASIHTTPRequest you can close the connection when you've received as much data as you want.
You could use NSString methods to retrieve the first 100 characters but you would have wasted bandwidth anyway to get all the data. So why download all when you want only 100 chars.
So to get only a slice of the data coming off the server, you need count the data stream that the url response gives. For this you could use NSURLConnection -
- (void)viewDidLoad {
[super viewDidLoad];
responseData = [[NSMutableData data] retain];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://someurl.com/data.json"]];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[responseData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
if([responseData length] <= 100)
[responseData appendData:data];
else //break connection
[self connectionDidFinishLoading:connection];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
label.text = [NSString stringWithFormat:#"Connection failed: %#", [error description]];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[connection release];
}
So you need to put your logic in didReceiveData. For here, you want only 100 chars, so break-off the connection after that number is reached.