Method gets called two times - objective-c

I am doing a NSURLConnection that downloads a file only if there is a new one (checked with the last-modified date). But to accomplish this I am using two methods with two different NSURLRequests and NSURLConnection. Well, they do the same.
- (IBAction)uppdatera:(id)sender
{
checkHeaders = YES;
NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.forellgatan.se/site/ftp_files/Kapareskolan.zip"] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10.0];
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection)
{
self.receivedData = [[NSMutableData data] retain];
}
else
{
UIAlertView *connectFailMessage = [[UIAlertView alloc] initWithTitle:#"Ingen internetanslutning! 1" message:#"Anslut dig till internet för att ladda ner!" delegate: self cancelButtonTitle:#"Ok" otherButtonTitles: nil];
[connectFailMessage show];
[connectFailMessage release];
}
}
- (void)downloadNewFile
{
NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.forellgatan.se/site/ftp_files/Kapareskolan.zip"] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10.0];
NSURLConnection *theConnection2 = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection2)
{
self.receivedData = [[NSMutableData data] retain];
}
else
{
UIAlertView *connectFailMessage = [[UIAlertView alloc] initWithTitle:#"Ingen internetanslutning! 2" message:#"Anslut dig till internet för att ladda ner!" delegate: self cancelButtonTitle:#"Ok" otherButtonTitles: nil];
[connectFailMessage show];
[connectFailMessage release];
}
checkHeaders = NO;
self.progressView.hidden = NO;
}
It goes through the didReceiveResponse method:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
if (checkHeaders == YES)
{
NSHTTPURLResponse *test = (NSHTTPURLResponse *)response;
if ([test respondsToSelector:#selector(allHeaderFields)])
{
NSDictionary *metaData = [test allHeaderFields];
NSString *lastModifiedString = [metaData objectForKey:#"Last-Modified"];
NSString *savedString = [[NSUserDefaults standardUserDefaults] stringForKey:#"LastModified"];
if (![lastModifiedString isEqualToString:savedString])
{
[self downloadNewFile];
}
else if ([lastModifiedString isEqualToString:savedString])
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Ingen uppdatering tillgänglig" message:#"Det finns ingen uppdatering att hämta just nu." delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[alert show];
[alert release];
}
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
[standardUserDefaults setObject:lastModifiedString forKey:#"LastModified"];
[standardUserDefaults synchronize];
}
}
[self.receivedData setLength:0];
self.fileSize = [[NSNumber numberWithLong: [response expectedContentLength]] retain];
}
Last the connectionDidFinishLaunching method:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if (checkHeaders == NO)
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
[self.receivedData writeToFile:[basePath stringByAppendingPathComponent:#"Kapareskolan.zip"] atomically:YES];
[self unzipDownloadedFile];
self.progressView.hidden = YES;
NSLog(#"Finished...");
}
[connection cancel];
}
I know the didFinishLaunching method gets called twice, but I want to know how I could get so the method doesn't get called twice if there is an update?
I know it's a lot asked and much code here but just give me a hint and I'll be very thankful.

If you're done with the first connection object in the didReceiveResponse method you should cancel it then. Otherwise it's going to make it to the connectionDidFinishLoading method.
I think this is what you want:
if (![lastModifiedString isEqualToString:savedString])
{
[connection cancel];
[self downloadNewFile];
}
Also It looks like you're setting checkHeaders to NO after you start the second request which could cause a race condition.

According to the programming guide, a connection "can be canceled any time before the delegate receives a connectionDidFinishLoading: or connection:didFailWithError: message by sending the connection a cancel message".
so why not try moving
[connection cancel];
from the connectionDidFinishLoading method to just after your if-else block in the didReceiveResponse delegate method? You want to cancel the "checkHeaders==YES" connection in either case; either you're about to kick off a new connection, or you already know all you need to know about the current connection.
UPDATED as requested:
if (![lastModifiedString isEqualToString:savedString]) {
[self downloadNewFile];
} else { // you've already implicitly checked for equality above
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Ingen uppdatering tillgänglig" message:#"Det finns ingen uppdatering att hämta just nu." delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[alert show];
[alert release];
}
// you've used the connection for everything that you need, so cancel it in either case
[connection cancel];
as downloadNewFile kicks off its NSURLConnection asynchronously, this should be okay in the event that the two strings are equal. Slightly safer would be to move the cancel method call to just prior to the if-else check.

Related

Send any file using xmpp in cocoa application. Is it possible?

In my chat application, I am unable to send any image or file while chat. What i tried is ---
Method 1...
NSXMLElement *body = [NSXMLElement elementWithName:#"body"];
[body setStringValue:#"Send Image Testing"];
NSXMLElement *message = [NSXMLElement elementWithName:#"message"];
[message addAttributeWithName:#"type" stringValue:#"chat"];
[message addAttributeWithName:#"to" stringValue:[jid full]];
[message addAttributeWithName:#"from" stringValue:[[xmppStream myJID] full]];
[message addChild:body];
NSImage *img = [NSImage imageNamed:#"loginLogo.png"];
NSData *imageData = [img TIFFRepresentation];
NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData:imageData];
NSData *data = [imageRep representationUsingType:NSJPEGFileType properties:nil];
NSString *imgStr = [NSString encodeBase64WithData:data];
NSXMLElement *ImgAttachement = [NSXMLElement elementWithName:#"attachment"];
[ImgAttachement setStringValue:imgStr];
[message addChild:ImgAttachement];
[xmppStream sendElement:message];
I added a "xmlElement" named "attachment" in "message" xmlElement. String value of "attachment" is ImageDataString encoded in "Base64" format. But this code is sending only the text to other end(not image).
Don't know the cause of failure, may be i should send NSImage or server link of the image in place of image data.
Method 2...
I also tried "XMPPOutgoingFileTransfer" classes, with following code.
[_fileTransfer sendData:decodedData
named:#"hello"
toRecipient:[XMPPJID jidWithString:#"MYUSERNAME#chat.facebook.com/RESOURCENAME"]
description:#"Baal's Soulstone, obviously."
error:&err])
But every time this is giving the same error - Error Domain=XMPPOutgoingFileTransferErrorDomain Code=-1 "Unable to send SI offer; the recipient doesn't have the required features."
Please help, if any idea
Thanks in advance
I got it working this way-
Inside setupStrem method, set up the incoming end like this -
xmppIncomingFileTransfer = [[XMPPIncomingFileTransfer alloc] init];
xmppIncomingFileTransfer.disableIBB = NO;
xmppIncomingFileTransfer.disableSOCKS5 = NO;
[xmppIncomingFileTransfer activate:xmppStream];
[xmppIncomingFileTransfer addDelegate:self delegateQueue:dispatch_get_main_queue()];
Implement the incoming end delegate methods-
- (void)xmppIncomingFileTransfer:(XMPPIncomingFileTransfer *)sender didFailWithError:(NSError *)error
{
DDLogVerbose(#"%#: Incoming file transfer failed with error: %#", THIS_FILE, error);
}
- (void)xmppIncomingFileTransfer:(XMPPIncomingFileTransfer *)sender didReceiveSIOffer:(XMPPIQ *)offer
{
DDLogVerbose(#"%#: Incoming file transfer did receive SI offer. Accepting...", THIS_FILE);
[sender acceptSIOffer:offer];
}
- (void)xmppIncomingFileTransfer:(XMPPIncomingFileTransfer *)sender didSucceedWithData:(NSData *)data
named:(NSString *)name
{
DDLogVerbose(#"%#: Incoming file transfer did succeed.", THIS_FILE);
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
NSString *fullPath = [[paths lastObject] stringByAppendingPathComponent:name];
[data writeToFile:fullPath options:0 error:nil];
DDLogVerbose(#"%#: Data was written to the path: %#", THIS_FILE, fullPath);
}
Incoming files will be written to the documents directory, you can update UI when it is done.
On the sending side-
if (!_fileTransfer) {
_fileTransfer = [[XMPPOutgoingFileTransfer alloc] initWithDispatchQueue:dispatch_get_main_queue()];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[_fileTransfer activate:appDelegate.xmppStream];
_fileTransfer.disableIBB = NO;
_fileTransfer.disableSOCKS5 = NO;
[_fileTransfer addDelegate:self delegateQueue:dispatch_get_main_queue()];
}
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
NSString *fullPath = [[paths lastObject] stringByAppendingPathComponent:filename];
NSData *data = [NSData dataWithContentsOfFile:fullPath];
NSError *err;
if (![_fileTransfer sendData:data named:filename toRecipient:[XMPPJID jidWithString:self.contact.primaryResource.jidStr] description:#"Baal's Soulstone, obviously." error:&err]) {
DDLogInfo(#"You messed something up: %#", err);
}
Implement the outgoing delegate methods -
- (void)xmppOutgoingFileTransfer:(XMPPOutgoingFileTransfer *)sender didFailWithError:(NSError *)error
{
DDLogInfo(#"Outgoing file transfer failed with error: %#", error);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"There was an error sending your file. See the logs." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
- (void)xmppOutgoingFileTransferDidSucceed:(XMPPOutgoingFileTransfer *) sender
{
DDLogVerbose(#"File transfer successful.");
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Success!" message:#"Your file was sent successfully." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
Note that the disableIBB and disableSOCKS5 should match at both ends.
If the problem still exists, go to XMPPOutgoingFileTransfer.m and then to the method-
- (void)handleRecipientDiscoInfoQueryIQ:(XMPPIQ *)iq withInfo:(XMPPBasicTrackingInfo *)info
Then put a NSLOG/Breakpoint at this line -
hasSOCKS5 = hasSI && hasFT && hasSOCKS5;
hasIBB = hasSI && hasFT && hasIBB;
Both values should become TRUE when sending a file. Check which one is FALSE (causing the error), you will get an idea why the incoming end is sending FALSE instantly. Try to fix that.

Button press doesn't work during parsing

I cannot make back button (previous page in navigation) work during html parsing process. What I am trying to do is parsing an html and get a pdf file on viewDidAppear. During that time I want back button to be enabled because parsing can take long time. So users can decide to leave parsing process.
here is my code:
-(void)viewDidAppear:(BOOL)animated
{
[self performSelector:#selector(getPDFUrl) withObject:nil afterDelay:0];
}
-(void) getPDFUrl
{
NSURL *programURL = [NSURL URLWithString:#"http://www.example.com/somepdf/"];
NSData *programHtmlData;
#try
{
programHtmlData = [NSData dataWithContentsOfURL:programURL];
}
#catch(NSException* ex)
{}
// 2
TFHpple *programHTMLParser = [TFHpple hppleWithHTMLData:programHtmlData];
NSString *studiosXpathQueryString =
#"//div[#class='ultra_wrapper']/div[#class='container columns extra_pad boxed_lay centered']/div[#id='prk_ajax_container']/div[#id='centered_block']/div[#id='main_block']/div[#id='content']/div[#id='main']/div[#class='twelve columns sidebarized']/div[#class='prk_no_composer']/p/a";
NSArray *programNodes = [programHTMLParser searchWithXPathQuery:studiosXpathQueryString];
NSMutableArray *activities = [[NSMutableArray alloc] init];
Tutorial *tutorial;
if (programNodes.count > 0) {
for (TFHppleElement *element in programNodes)
{
#try
{
tutorial = [[Tutorial alloc] init];
tutorial.url = [element objectForKey:#"href"];
}
#catch(NSException* ex)
{
}
}
NSURL *targetURL = [NSURL URLWithString:tutorial.url];
webView.scalesPageToFit=YES;
NSURLRequest *request = [NSURLRequest requestWithURL:targetURL];
[webView loadRequest:request];
}
else
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"warning"
message:#"warning!"
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
}
[self performSelector:#selector(getPDFUrl) withObject:nil afterDelay:0];
will execute on main thread. Therefore Blocking your UI.
You should use
[self performSelectorInBackground:#selector(getPDFUrl) withObject:nil];
Or you can use NSOperationQueue or just NSOperation or more simple way NSBlockOperation.
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
NSLog(#"dasds");
}];
[op start];

Display a UIAlertView if no new posts were found

I am developing an app for my website that has an RSS Feed with a parser. I made a refresh button to look for new posts and it works. But now, I want it to display a UIAlertView that says "No posts found" if there were no new posts found.
This is my refresh button
- (IBAction)refreshButton:(UIBarButtonItem *)sender
{
// 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 -
// Note we can concatenate literal strings together on multiple lines in this way it
// results in a single NSString instance
NSURL *url = [NSURL URLWithString:
#"http://sephardijews.com/feed/"];
// Putting the URL we made into an NSURLRequest, so we can connect to the url data that we specifed
NSURLRequest *req = [NSURLRequest requestWithURL:url];
// Creating a connecting that will exchange this request for the data from the URL we specifed
connection = [[NSURLConnection alloc] initWithRequest:req
delegate:self
startImmediately:YES];
[[self tableView] reloadData];
NSLog(#"%#\n %#\n %#\n", channel, [channel title], [channel infoString]);
}
How could I do this? Something with an if statement right?
The refresh button:
- (IBAction)refreshButton:(UIBarButtonItem *)sender
{
// 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 -
// Note we can concatenate literal strings together on multiple lines in this way it
// results in a single NSString instance
NSURL *url = [NSURL URLWithString:
#"http://sephardijews.com/feed/"];
// Putting the URL we made into an NSURLRequest, so we can connect to the url data that we specifed
NSURLRequest *req = [NSURLRequest requestWithURL:url];
// Creating a connecting that will exchange this request for the data from the URL we specifed
connection = [[NSURLConnection alloc] initWithRequest:req
delegate:self
startImmediately:YES];
[[self tableView] reloadData];
NSLog(#"%#\n %#\n %#\n", channel, [channel title], [channel infoString]);
if ([[self tableView] numberOfRowsInSection:0] > someNumberVariableForLastCount) {
someNumberVariableForLastCount = [[self tableView] numberOfRowsInSection:0];
[[self tableView] reloadData];
}else{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"No New Posts" message:#"There were no new posts found" delegate:self cancelButtonTitle:#"Dismiss" otherButtonTitles:nil, nil];
[alert show];
}
}
Your reloadData call should occur in connectionDidFinishLoading, not right after you start the connection (it is an asynchronous network call, the table will be reloaded before anything is even fetched). The alert should be kicked off there too. You need to implement all the connection delegate stuff, hopefully you've done that. If not, basic example here.
This will compare the current row count in a section you specify to a variable for the last count:
if ([[self tableView] numberOfRowsInSection:0] > someNumberVariableForLastCount) {
someNumberVariableForLastCount = [[self tableView] numberOfRowsInSection:0];
[[self tableView] reloadData];
}else{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"no new items" message:#"OH NO!!" delegate:self cancelButtonTitle:#"Dismiss" otherButtonTitles:nil, nil];
[alert show];
}

How could connectionDidFinishLoading: run if no file is found on server? [duplicate]

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Testing use of NSURLConnection with HTTP response error statuses
This is bizarre; I have an async connection like so:
NSString *url=[NSString stringWithFormat:#"http://www.whatever.com/file"];
NSURL *url2=[NSURL URLWithString:url];
NSURLRequest *req=[[NSURLRequest alloc] initWithURL:url2];
NSURLConnection*con=[[NSURLConnection alloc] initWithRequest:req delegate:self];
[req release];
if(con){
NSMutableData *data=[[NSMutableData alloc] init];
self.receivedData=data;
[data release];
}
else {
UIAlertView*alert=[[UIAlertView alloc] initWithTitle:#"Error!" message:#"Unable to connect to server." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
[alert release];
}
Then I have a bunch of standard delegate methods:
-(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];
self.receivedData=nil;
UIAlertView*alert=[[UIAlertView alloc] initWithTitle:#"This app requires Internet" message:[NSString stringWithFormat: #"Connection failed.\n Please exit and check your\nInternet access."] delegate:self cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
[alert release];
}
-(void) connectionDidFinishLoading:(NSURLConnection *)connection{
NSString *payload=[[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding];
self.downloaded=nil;
self.downloaded=payload;
[payload release];
[connection release];
self.receivedData=nil;
NSLog(#"This will display if connectionDidFinishLoading runs.");
}
The NSLog there at the end is running, even if the file is not on the server. Why? I would not expect it to have loaded anything, and instead resulted in an error and an alert view.
These methods seem clear at first, but there must be something going on here that I'm not getting about the async connection.
It DID finish loading. The fact that it finished with an HTTP error is beside the point.
The NSHTTPResponse object you got in didReceiveResponse will have a .responseCode property with the 404 in it.

Authenticating Iphone Login with PHP/MySQL and HTTP Responses

Im trying to create a iPhone Objective C login page using PHP/MySQL to authenticate. Im trying to use HTTP responses as a way of authenticating. Right now it always fails to login , regardless of what you enter.
`- (IBAction) login: (id) sender
{
// this part isnt really implemented yet!
NSString *post =[NSString stringWithFormat:#"username=%#&password=%#",usernameField.text, passwordField.text];
NSURL *url = [NSURL URLWithString:#"MY URL HERE.php"];
NSURLRequest *request = [NSURLRequest requestWithURL: url];
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)request;
responseStatusCode = [httpResponse statusCode];
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:response error:nil];
if ([responseStatusCode statusCode] == 200) {
//do something
UIAlertView *alertsuccess = [[UIAlertView alloc] initWithTitle:#"success" message:#"Your have logged in" delegate:self cancelButtonTitle:#"No" otherButtonTitles:#"Yes", nil];
[alertsuccess show];
[alertsuccess release];
}
else {
NSLog(#"Login failed.");
//do something else
UIAlertView *alertfail = [[UIAlertView alloc] initWithTitle:#"fail" message:#"login fail" delegate:self cancelButtonTitle:#"No" otherButtonTitles:#"Yes", nil];
[alertfail show];
[alertfail release];
}
`
and the PHP looks like
<?
session_start();
require("iphoneconnection.php");
$u = $_POST['username'];
$pw = $_POST['password'];
$check = "SELECT username, password FROM iphoneusers WHERE username='$u' AND password= '$pw'";
$login = mysql_query($check, $connect) or die(mysql_error());
// check user level and store in $row
if (mysql_num_rows($login) == 1) {
$row = mysql_fetch_assoc($login);
//$_SESSION['level'] = $row['level'];
$_SESSION['username'] = $u;
echo 'login success';
header("HTTP/1.1 200 OK");
//header("Location: index.php");
} else {
//header("Location: login.php");
//echo '403 Forbidden';
header("403 Forbidden");
}
mysql_close($connect);
?>
Anyone got any pointers? Even if its just to tell me if Im formatting my repsonses correctly in the PHP.
Thanks in advance.
Thanks for the response. I got it to work using:
- (IBAction) login: (id) sender
{
NSString *post =[NSString stringWithFormat:#"username=%#&password=%#",usernameField.text, passwordField.text];
NSString *hostStr = #"MY URL.php?";
hostStr = [hostStr stringByAppendingString:post];
NSData *dataURL = [NSData dataWithContentsOfURL: [ NSURL URLWithString: hostStr ]];
NSString *serverOutput = [[NSString alloc] initWithData:dataURL encoding: NSASCIIStringEncoding];
if([serverOutput isEqualToString:#"Yes"]){
UIAlertView *alertsuccess = [[UIAlertView alloc] initWithTitle:#"success" message:#"You are authorized"
delegate:self cancelButtonTitle:#"No" otherButtonTitles:#"Yes", nil];
[alertsuccess show];
[alertsuccess release];
} else {
UIAlertView *alertsuccess = [[UIAlertView alloc] initWithTitle:#"Fail" message:#"Invalid Access"
delegate:self cancelButtonTitle:#"No" otherButtonTitles:#"Yes", nil];
[alertsuccess show];
[alertsuccess release];
}
loginIndicator.hidden = FALSE;
[loginIndicator startAnimating];
loginButton.enabled = FALSE;
}
According to the sample you've given, I don't see that you're actually sending the username & password with the request. You're creating a string "post", but never including it in the request.
Also, this cast:
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)request;
doesn't look right. Why are you typecasting the request object to a response?