NSURLRequest cachePolicy and connection in UIWebView - objective-c

I set up a UIWebView with a cachePolicy of NSURLRequestReloadIgnoringLocalAndRemoteCacheData, and everything was fine: when there was a connection, the UIWebView loaded, and when there wasn't, connection:didFailWithError: was called and I got my UIAlertView.
But when I change the cachePolicy to NSURLRequestReturnCacheDataElseLoad (which is the policy I actually want), in the absence of a connection, the cached page loads and then, when I click on a link...nothing happens. No connection:didFailWithError: called. No UIAlertView.
How do I fix this?
EDIT: Perhaps I have the beginning of an answer.... I can at least identify when a link in the UIWebView is clicked, using the following method:
- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)req navigationType: (UIWebViewNavigationType)navigationType
{
if (navigationType==UIWebViewNavigationTypeLinkClicked)
{
NSLog(#"Blah!");
}
return YES;
}
Now, instead of NSLogging "Blah!" I just need to get it to call connection:didFailWithError.
So how do I do that?

I did it!
I added this code:
- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)req navigationType:(UIWebViewNavigationType)navigationType
{
if (navigationType==UIWebViewNavigationTypeLinkClicked)
{
NSURL *scriptUrl = [NSURL URLWithString:#"http://www.homePageOfTheApp.com"];
NSData *data = [NSData dataWithContentsOfURL:scriptUrl];
if (data == nil)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error title." message:#"Error message." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
}
return YES;
}
Though I might replace http://www.homePageOfTheApp.com" with http://www.google.com since, even though my host has very reliable servers, I'm going to guess that Google's are even better....

Related

Manage view from other class

I have several view controllers that need to make Parse services calls, which is therefore handled in a class for every "Parse" related work.
Now I have a case where the parse response will (if no errors comes up obviously) instantiate the MFMessageComposeViewController with certain parameters.
The text body comes from parse, the recipients comes from the class.
My problem is : I have copy/pasted code that I can't get rid off because I don't know what to do
In my controller class, I do this :
- (void)showSMS:(NSString*)reciever { //This is the phone number
if(![MFMessageComposeViewController canSendText]) {
UIAlertView *warningAlert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"ALERT_ERROR_Title", nil) message:NSLocalizedString(#"ALERT_ERROR_NOSMS", nil) delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[warningAlert show];
return;
}
[[ParseService sharedInstance]sendSMSOfType:SMS_TYPE_INSIST toRecipient:reciever andBlock:^(NSError *error, NSString *body) {
if (!error){
MFMessageComposeViewController *messageController = [[MFMessageComposeViewController alloc] init];
messageController.messageComposeDelegate = self;
NSArray *recipents = [[NSArray alloc]initWithObjects:reciever, nil];
[messageController setRecipients:recipents];
[messageController setBody:body];
[self presentViewController:messageController animated:YES completion:nil];
}else{
UIAlertView *warningAlert = [[UIAlertView alloc] initWithTitle:#"Error getting sms" message:#"Couldn't get the text online" delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[warningAlert show];
return;
}
}];
}
and I have this exact same block of code in 3 different places, except for ONE difference, the SMS_TYPE parameter.
How should I tweak this so I don't have to create and assign the parameters in every controller, have one neat line that would look like this [??? setUpMessageControllerWithParam:recipients:body];
Pretty much the whole completion block here troubles me, but I can't really put it somewhere else because I'll have trouble presenting a view on self if self isn't a VC, same goes for delegates and alerts.
This is probably simple for most of you, I'm just OOP-retarded. Any help is much appreciated :)
There are several ways to do the this ... like creating a base view controller with this method passing the custom parameters and inheriting your other view controllers from it, or you can make a Category with this method and import this category and use wherever you need ...

The uiactionsheet crash

I have an action sheet which works like this:
- (void)Method1 {
UIActionSheet *photoSourceSheet=[[UIActionSheet alloc]
initWithTitle:#"Options"
delegate:self
cancelButtonTitle:#"Exit"
destructiveButtonTitle:nil
otherButtonTitles:#"opt1",#"opt2", #"opt3", nil];
photoSourceSheet.tag=1;
photoSourceSheet.delegate=self;
[photoSourceSheet showInView:self.view];
}
- (void)Method2 {
UIActionSheet *photoSourceSheet1=[[UIActionSheet alloc]
initWithTitle:#"Select Video"
delegate:self
cancelButtonTitle:#"Cancel"
destructiveButtonTitle:nil
otherButtonTitles:#"Take New Video", #"Choose Existing Video", nil];
// photoSourceSheet.delegate=self;
photoSourceSheet1.tag=2;
photoSourceSheet1.delegate=self;
[photoSourceSheet1 showInView:self.view];
}
then in my delegate i have:
- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex: NSInteger)buttonIndex {
if (actionSheet.tag==1) {
if (buttonindex==2) {
[self method2];
}
} else if (actionSheet.tag==2) {
// some code
}
}
My delegate method gets called for the first action sheet i.e. photoSourceSheet but not for photoSourceSheet1.
Is there something special I need to do, like manually dismissing the sheet?
My second UIActionSheet (photoSourceSheet1) appears but it crashes the app as soon as I select an option on the sheet.
It throws EXEC_BAD_ACCESS
There is nothing wrong with the above code.
EXEC_BAD_ACCESS is basically due to ill Memory Management.Sometimes you unintentionally remove the object that is being used.
Try enable Zombies it will tell you the exact problem area.
Steps: Go to Edit Scheme
Memory Management
Check the option enable Zombie Objects

confirmation before open in safari

Im trying to add a UIAlertView to warn the user that the link will open in Safari. The user can then choose OK(open the url) or cancel which should just close the alert and return to the links.
I have three different UIButtons which has 3 different URLs.
Right now ive added a IBAction to all buttons and all buttons has Tags (which i think i can use somehow :D). I guess - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex: delegate will be good to use to..
My question: how should the UIAlertView know what URL to open i user clicks ok?
I suggest that you use a subclass of UIAlertView that is able to track some more properties along. I do this in all my projects and it is much simpler.
One solution to do this would be to subclass UIAlertView to MyAlertView and add a #property(nonatomic, retain) id userInfo; or #property(nonatomic, retain) NSURL* urlToOpen. Thus you can attach custom data to your UIAlertView and retrieve it in the delegate method to do whatever you need with it.
Another solution, and my preferred one actually, is to add Objective-C blocks support to UIAlertView, to be able to use UIAlertView using the blocks API instead of using a delegate. This is particularly useful if you use multiple UIAlertViews in the same class and with the same delegate, as using a single delegate to handle the different instances is a mess.
I personally use this technique all the time, as it also makes my code more readable by having the code that executes when the button is tapped right next to the code that shows the alert, instead of having it at a complete different places when you use delegate methods.
You can look at my OHAlertView subclass here on GitHub that implement this already. The usage is really simple and allow you to use blocks for each alert view instead of a common delegate, see below.
Usage Example
-(void)confirmOpenURL:(NSURL*)url
{
NSString* message = [NSString string WithFormat:#"Open %# in Safari?",
url.absoluteString];
[OHAlertView showAlertWithTitle:#"Open URL"
message:message
cancelButton:#"No"
okButton:#"Yes"
onButtonTapped:^(OHAlertView* alert, NSInteger buttonIndex)
{
if (buttonIndex != alert.cancelButtonIndex)
{
// If user tapped "Yes" and not "No"
[[UIApplication sharedApplication] openURL:url];
}
}];
}
Then each button can have its own action:
-(IBAction)button1Action
{
[self confirmOpenURL:[NSURL URLWithString:#"http://www.google.com"]];
}
-(IBAction)button2Action
{
[self confirmOpenURL:[NSURL URLWithString:#"http://www.stackoverflow.com"]];
}
Or you can have a common IBAction for all your buttons opening URLs:
-(IBAction)commonButtonAction:(UIButton*)sender
{
NSUInteger tag = sender.tag;
NSString* urls[] = { #"http://www.google.com", #"http://www.stackoverflow.com" };
NSURL* buttonURL = [NSURL URLWithString: urls[tag] ]; // in practice you should check that tag is between 0 and the number of urls to be sure, that's just an example here
[self confirmOpenURL:buttonURL];
}
Solved it like this:
added a tag when creating the UIAlertView. Like this:
- (IBAction)PressedButton {
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"Link"
message:#"Want to open in safari?"
delegate:self
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
message.tag = 2; //different tag for each button
[message addButtonWithTitle:#"Cancel"];
[message show];
}
Then when - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex delegate was thrown I did this:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == alertView.cancelButtonIndex){
if (alertView.tag == 1)
{
//go to URL1
}
else if (alertView.tag == 2)
{
//go to URL2
}
else if (alertView.tag == 3)
{
//go to URL3
}
}
}
Your button action method should have a signature like this:
-(void)doSomething:(id)sender;
whereby sender will be the button. Based on this you could find out which URL is meant.

Strange error when dismissing MFMailComposeViewController: error: address doesn't contain a section that points to a section in a object file

I am getting a really weird error MFMailCompseViewController. The error is "error: address doesn't contain a section that points to a section in a object file". The app crashes after MFMailCompseViewController dismisses and the email actually get sent.
This is specific to MFMailComposeViewController as I have tried to present a plain view controller modally and it dismisses fine.
Here is the code I wrote to calland present mail composer:
- (void) emailImage:(UIImage *)img {
//verified that the image is being returned correctly
UIImage *img1 = [[_delegate photoBrowser:self photoAtIndex:0] underlyingImage];
MFMailComposeViewController *mfViewController = [[MFMailComposeViewController alloc] init];
mfViewController.mailComposeDelegate = self;
NSString *subject = #"Check out this photo I took - Cap That App";
[mfViewController setSubject:subject];
NSData *imgData = UIImageJPEGRepresentation(img1, 1.0);
[mfViewController addAttachmentData:imgData mimeType:#"image/jpg" fileName:#"photo.jpg"];
NSString *contactMessage = #"\n\nSent via Cap That - Available in the Apple App Store";
[mfViewController setMessageBody:contactMessage isHTML:YES];
[self presentViewController:mfViewController animated:YES completion:nil];
}
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error {
UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:#"Status:" message:#"" delegate:nil cancelButtonTitle:#"ok" otherButtonTitles:nil] autorelease];
switch (result) {
case MFMailComposeResultCancelled:
alert.message = #"You chose not to send the email.";
break;
case MFMailComposeResultSaved:
alert.message = #"Your email was saved as a draft. It has not been sent yet.";
break;
case MFMailComposeResultSent:
alert.message = #"Your email has been sent!";
break;
case MFMailComposeResultFailed:
alert.message = #"There was an error sending the email. Please verify your email is working and try again.";
break;
default:
alert.message = #"You chose not to send the email.";
break;
}
[self dismissViewControllerAnimated:YES completion:^(void) {
[alert show];
}];
}
Thanks in advance for anyone's help on this.
I'm getting the same error in my app, acting on a tap gesture on a HUD. My gesture recognizer method is using a block property on the HUD to perform the needful actions, and there's where it's crashing (the code within the block never gets to run). Apparently the program cannot access that code, and since you also have a completion block that might be a clue to what's happening.
I don't see that I'm doing anything wrong in my code, and you don't seem to be doing that either, so maybe it's a bug. Are you by any chance running a developer preview of Xcode (4.4 or 4.5)?
Edit: it turns out my problem was that the code block property was being released before it got the chance to run. I think a similar thing might happend in your case, with the alert var. Can you try moving the alert init within the completion block?
Edit 2: As an alternative, try prefixing the alert init with __weak (or __unsafe_unretained if you're targetting iOS 4.3), that should do it. If you're not using ARC, use __block instead.

How to let the user fill the rest of the url link in xcode?

I am working with xcode and I'm stuck in writing URL links. I want the user to fill the rest of my written URL code.
Here's the code:
- (IBAction)openURL:(id)sender {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"http://facebook.com/....."]];
}
The user will see this:
facebook.com/write your address here...
The "facebook.com/" part is a label and the "write your address here..." a textfield that is filled in the apps settings. For example I can write "JJMLS" in settings, so the result is "facebook.com/JJMLS".
I have sat a custom/invisibile "openURL" button over the result area.
How can I tell xcode that #"http://facebook.com/(.....)" part is an area that should get the JJMLS result when the user fills it???
I will really appreciate some help!
-JJ
Try something like this:
- (IBAction)openURL:(id)sender {
if(textfield.text == nil){
UIAlertView *noTextAlert = [UIAlertView initWithTitle: #"No text entered!"
message: #"Please enter some text"
delegate: self
cancelButtonTitle: #"Ok"
otherButtonTitles: nil];
[noTextAlert show];
}
else{
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:#"http://facebook.com/%#", textfield.text]]];
}
}
If you have any questions about the code, just put it in the comments. Hope this helps!