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];
}
Related
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];
OK, I have a login screen that sends username and password to a PHP page. The PHP page echo's Yes or No based on the login status, but I would like it to echo the user id from the database for further use. Do I have to use JSON for this? I dont know how to retrieve more than one echo's. Currently the code is:
- (IBAction) login: (id) sender
{
NSString *post =[NSString stringWithFormat:#"username=%#&password=%#",usernameField.text, passwordField.text];
NSString *hostStr = #"https://www.mysite.com/login.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:#"Business Manager" message:#"Login Successful"
delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alertsuccess show];
[alertsuccess release];
RootViewController *viewMenu = [[RootViewController alloc]
initWithNibName:#"RootViewController" bundle:[NSBundle mainBundle]];
self.rootViewController = viewMenu;
[viewMenu release];
[self.navigationController pushViewController:self.rootViewController animated:YES];
} else {
UIAlertView *alertsuccess = [[UIAlertView alloc] initWithTitle:#"Error" message:#"Username or Password Incorrect"
delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alertsuccess show];
[alertsuccess release];
}
}
Now I just want to pull one more echo called "ID". Forming JSON for this seems arbitrary.
Technically you can use whatever response format you want -- though it's best to stick with something standard as you'll be able to find libraries made for you already. A REST service with a JSON response is a pretty easy way to facilitate communication between a server and client, so it would probably be a good idea to start there.
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.
I am trying to access the XML http://webservices.ns.nl/ns-api-stations using ASIHTTPRequest. But what I am using now doesn't seem to work. It says the host is not reachable. So I assume it is going wrong at the ASIHTTPRequest part?
-(void)fetchStationData {
//Method for the fetching of the data
//First lets check wheater there is an internet connection and if the host is reachable
if(internetActive) {
//Internet is active
//Init the parser
parser = [[RSSParser alloc] init];
//Set he parser context
parser.context = context;
//The array to het the data from
NSURL *url = [NSURL URLWithString:#"http://webservices.ns.nl/ns-api-stations"];
ASIHTTPRequest *requestaccount = [ASIHTTPRequest requestWithURL:url];
[requestaccount setUsername:#"user"];
[requestaccount setPassword:#"password"];
//The XML elements to fetch
NSArray *elements = [[NSArray alloc] initWithObjects:#"name",nil];
//The actual fetchin
[parser fetchStationItemsForUrl:url forElements:elements];
//Save the context ?
[context save:nil];
//Clean up
[elements release];
}else{
//Internet is down :(
//Offline artikelen inladen
//Dit uitvoeren op de main que
dispatch_async(dispatch_get_main_queue(), ^ {
UIAlertView *Notpermitted = [[UIAlertView alloc] initWithTitle:#"Alert" message:#"Er is geen verbinding mogelijk met de Mezz. Offline artikelen zijn ingeladen." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[Notpermitted show];
[Notpermitted release];
});
}
}
Use ASIFormDataRequest.
NSURL *url = [NSURL URLWithString:#"http://webservices.ns.nl/ns-api-stations"];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request addPostValue:#"accountname" forKey:#"username"];
[request addPostValue:#"password" forKey:#"password"];
[request setCompletionBlock:^ {
// do something when request succeeds and credentials are ok, e.g. redirect user to the home page
}];
[request setFailedBlock:^ {
// notify user that request failed
}];
[request startAsynchronous];
This question already has an answer here:
Closed 11 years ago.
Possible Duplicate:
Can't get Twitter OAuth callback authentication to work in Cocoa application
I have an application that sends Twitter updates (tweets). The current version does OAuth authentication using the OOB process:
(1) Send request token request:
consumer = [[OAConsumer alloc] initWithKey: kOAuthConsumerKey secret:kOAuthConsumerSecret];
OAMutableURLRequest *request = [[[OAMutableURLRequest alloc]
initWithURL: [NSURL URLWithString:kOAuthTwitterRequestTokenURL]
consumer: self.consumer
token: nil realm: nil signatureProvider: nil] autorelease];
[request setHTTPMethod:#"POST"];
OADataFetcher *fetcher = [[[OADataFetcher alloc] init] autorelease];
[fetcher fetchDataWithRequest:request delegate:self didFinishSelector:#selector(setRequestToken:withData:) didFailSelector:#selector(failRequestToken:data:)];
(2) Then assemble the authentication URL and open a browser window:
NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
self.requestToken = [[[OAToken alloc] initWithHTTPResponseBody:dataString] autorelease];
NSString *urlString = [NSString stringWithFormat: #"%#?oauth_token=%#", kOAuthTwitterAuthorizeURL, self.requestToken.key];
NSURL *requestURL = [NSURL URLWithString: urlString];
[[NSWorkspace sharedWorkspace] openURL: requestURL];
(3) Pop up an Alert Window with a text field, and get the PIN number from the user, then:
self.requestToken.secret = [input stringValue];
OAMutableURLRequest *request = [[OAMutableURLRequest alloc] initWithURL:[NSURL URLWithString:kOAuthTwitterAccessTokenURL] consumer: self.consumer token: self.requestToken realm: nil signatureProvider: nil];
[request setHTTPMethod:#"POST"];
OADataFetcher *fetcher = [[[OADataFetcher alloc] init] autorelease];
[fetcher fetchDataWithRequest:request delegate:self didFinishSelector:#selector(setAccessToken:withData:) didFailSelector:#selector(failAccessToken:data:)];
(4) Save the return credentials:
NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
accessToken = [[OAToken alloc] initWithHTTPResponseBody:dataString];
[accessToken storeInUserDefaultsWithServiceProviderName: kOAuthTwitterDefaultsDomain prefix: kOAuthTwitterDefaultsPrefix];
(Memory Management and error checking removed for brevity)
This all works fine, but I really hate the process of redirecting to a browser and then making the user copy and paste the PIN number. I wanted to stop using the OOB method and go to the callback method. That takes some finagling for a Desktop application. So I switched to using a WebView, and I do steps 1 and 2 the same, except 2 does this instead:
NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
self.requestToken = [[[OAToken alloc] initWithHTTPResponseBody:dataString] autorelease];
OAMutableURLRequest* requestURL = [[[OAMutableURLRequest alloc] initWithURL:[NSURL URLWithString:kOAuthTwitterAuthorizeURL] consumer:nil token:self.requestToken realm:nil signatureProvider:nil] autorelease];
[requestURL setParameters:[NSArray arrayWithObject:[[[OARequestParameter alloc] initWithName:#"oauth_token" value: self.requestToken.key] autorelease]]];
[[webview mainFrame] loadRequest: requestURL];
[NSApp beginSheet: webSheet modalForWindow: [NSApp mainWindow] modalDelegate:self didEndSelector: #selector(webSheetDidEnd:returnCode:contextInfo:) contextInfo:nil];
I use the WebView PolicyDelegate to look for the redirect (to my callback URL) after the user has authorized using the Twitter webpage:
- (void)webView: (WebView *)webView decidePolicyForNavigationAction: (NSDictionary *)actionInformation request: (NSURLRequest *)request frame: (WebFrame *)frame
decisionListener: (id<WebPolicyDecisionListener>)listener
{
NSString *urlString = [[actionInformation objectForKey:#"WebActionOriginalURLKey"] absoluteString];
if ([urlString rangeOfString:#"oauth_verifier"].location != NSNotFound)
{
// parse out oauth_token and oauth_verifier from the URL
}
self.requestToken.secret = oauthVerifier;
[listener ignore];
[NSApp endSheet:webSheet returnCode: 0];
OAMutableURLRequest *request = [[[OAMutableURLRequest alloc] initWithURL:[NSURL URLWithString:kOAuthTwitterAccessTokenURL] consumer: self.consumer token: self.requestToken realm: nil signatureProvider: nil] autorelease];
[request setHTTPMethod:#"POST"];
OADataFetcher *fetcher = [[[OADataFetcher alloc] init] autorelease];
[fetcher fetchDataWithRequest:request delegate:self didFinishSelector:#selector(setAccessToken:withData:) didFailSelector:#selector(failAccessToken:data:)];
}
As far as I can tell, this should authenticate exactly the same. The only difference, as far as Twitter is concerned, is that I'm using the oauth_verifier from the callback URL instead of the one presented to the user (and provided back to my application). But if this was a true server-to-server implementation, that's the value I'd be using anyway.
What actually happens is that this last step (getting the access token) fails with:
failAccessToken: 'Error Domain=NSURLErrorDomain Code=-1012 "The operation couldn’t be completed. (NSURLErrorDomain error -1012.)" UserInfo=0x101b3e780 {NSErrorFailingURLStringKey=http://api.twitter.com/oauth/access_token, NSUnderlyingError=0x101bacf40 "The operation couldn’t be completed. (kCFErrorDomainCFNetwork error -1012.)", NSErrorFailingURLKey=http://api.twitter.com/oauth/access_token}'
Any ideas on what's causing this authentication to fail?
joe
I finally gave up on the OAuthConsumer framework and switched to the Google GTMOAuth framework. That works fine.