I'm working on this login screens. I want the user to be notified when login fails. For example if the user enters the wrong credentials or the server which my app connects to is down it has to show a message login failed.
This is my code so far:
-(void)loginSucces
{
[self showLoginProcess:false];
PaMainViewController * vc = [[PaMainViewController alloc] init];
[self.navigationController pushViewController:vc animated:YES];
}
-(void)loginFailed
{
//TODO: handle error
[self showLoginProcess:false];
NSLog(#"LoginVC Fail!");
}
i hope this is what u want..
Use UIAlertView if you just wants to notify the User that login is failed.
-(void)loginFailed
{
//TODO: handle error
[self showLoginProcess:false];
NSLog(#"LoginVC Fail!");
UIAlertView *alert=[[UIAlertView alloc]initWithTitle:#"Warning" message:#"LoginVC Fail!" delegate:self cancelButtonTitle:nil otherButtonTitles:#"OK", nil];
[alert show];
}
You can display UIAlertView with your message or you can use any third party toast notifications.
Best approach, when network request ends with some result, process that result and show it to user. Usually server provides some user readable information about reason of failure. Same story when for some reason server is not reachable, just take NSError and show user a localizedDescription.
Why this way. User will receive some useful information and you will have less headache with localization.
Good example: network is unavailable. In such case NSerror localizedDescription will provide nice explanation of the problem and user can do respective action like enable WiFi or go outside to restore 3g connection.
Related
I have a code in a class method that must show a UIAlertView on successful operation (it's a Facebook SDK handler).
But, the alert doesn't show up.
Here's the code:
-(void) postCompletionHandler:(FBRequestConnection *)connection
result:(id)
result error:(NSError*)error
{
if (!error) {
// Link posted successfully to Facebook
NSLog(#"result: %#", result);
UIAlertView *successAlert = [[UIAlertView alloc]
initWithTitle:#"FB Success!"
message:result
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[successAlert show];
}
}
I put a breakpoint inside the block and
[successAlert show];
reaches execution and successfully executes. The debugger also shows, that this code excuted from the main, GUI thread.
I can't even imagine what can be the problem here.
Any ideas?
Update: big thanks for everyone who participated, your help means so much for me.
Now I solved the problem. The problem was, as pointed by #rmaddy, in incorrect parameter result, given to a constructor of an alertView class.
In the future I'll investigate on exception handling techniques in Xcode to prevent such an obvious errors to ruin my code.
When something goes so horribly wrong that my app can't continue and needs to exit, I want to pop up an alert box to the user, and then close the app when they tap the OK button. Sounds simple enough, right?
But here's the problem: my fatal error handler gets called by a 3rd party library (I don't have their source code). I give them a pointer to my fatal error handler on initialization, and when they encounter a fatal error they simply call that routine and expect it to never return. If it returns, the 3rd party library will assume I've handled the error and it will continue on its way (possibly corrupting data because things are now in an inconsistent state). I could just exit the application at the end of my error handler (which is what they expect), but I want to be able to display a message to the user first to tell them what the problem is.
Unfortunately, if I just do:
-(void)fatalErrorHandler:(NSString *)msg
{
// Log the error and shut down all the things that need
// to be shut down before we exit
// ...
// Show an alert to the user to tell them what went wrong
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:msg delegate:self cancelButtonTitle:#"Close" otherButtonTitles:nil];
[alert show];
}
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
exit(-1);
}
fatalErrorHandler returns right after [alert show], which tells the 3rd party library that I've handled the error and it will continue on as if nothing has happened. This is no good.
I need to NOT return from fatalErrorHandler. Ever. But since I'm on the main thread, the UIAlertView won't appear until fatalErrorHandler returns. A catch-22.
Any ideas on how I can show an alert to the user without returning from my fatal error handler?
I don't know if this would work, but what about starting a while loop with a sleep in its body for, let's say, 1 second each cycle? The while would exit when a Bool variable would have been set to YES, maybe from the alertViewDelegate.
From what you wrote "fatalErrorHandler returns right after [alert show], which tells the 3rd party library that I've handled the error and it will continue on as if nothing has happened."
I guess what you actually need is to pause everything when the fatalErrorHandler method is called. To achieve this, you can stop all NSTimer, queued methods etc. before displaying alertView.
Alternatively, you can display alertView via a different thread, and then use usleep(long long time) to pause the thread where fatalErrorHandler is in.
Okay, wesley6j's answer gave me an idea. Here's what I came up with:
-(void)fatalErrorHandler:(NSString *)msg
{
// Pop up an alert to tell the user what went wrong. Since this error
// handler could be called from any thread, we have to make sure this happens
// on the main thread because it does UI stuff
[self performSelectorOnMainThread:#selector(showMessage:) withObject:msg waitUntilDone:YES];
// Now, if we're NOT on the main thread, we can just sleep forever and never
// return from here. The error handler will exit the app after the user
// dismisses the alert box.
if (![NSThread isMainThread])
Sleep(0x7fffffff);
else
{
// OTOH, if we ARE on the main thread, we have to get a bit creative.
// We don't ever want to return from here, because this is a fatal error
// handler and returning means the caller can continue on its way as if
// we "handled" the error, which we didn't. But since we're on the main
// thread, we can't sleep or exit because then the user will never see
// the alert box we popped up in showMessage. So we loop forever and
// keep calling the main run loop directly to let it do its processing
// and show the alert. This is what the main run loop does anyway, so
// in effect, we just become the main run loop.
for (;;)
{
[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate: [NSDate date]];
}
}
}
-(void)showMessage:(NSString *)msg
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:msg delegate:self cancelButtonTitle:#"Close" otherButtonTitles:nil];
[alert show];
[alert release];
}
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
exit(-1);
}
This works perfectly and does exactly what I need.
This is my code
audioViewController *voiceRecorder = [audioViewController sharedManager];
[voiceRecorder stopRecording];
NSString *msg = [NSString stringWithFormat:#"Want to logout?"];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Info"
message:msg
delegate:self
cancelButtonTitle:#"No"
otherButtonTitles:#"Yes", nil];
alert.tag = 100;
[alert show];
I am calling sharedManager in one of my view controller. The problem is, my alertview runs before sharedManager method executes, if you check my code, i have called "StopReording" method, but when i run the code, it works after showing alert. Anyone has idea, how do I show alert only after the method returns something.?
You seem to be confusing yourself about method run order and alert presentation order. The methods run in the order specified by your code, they must. What you see on screen is 2 alerts, one (stop) presented first, the the other (logout) presented immediately after.
Generally, you shouldn't show 2 alerts at the same time. Certainly not if they relate to different things.
Present your first alert, then wait for the answer to be received (using the delegate methods). Once you have the users answer, then decide what to do next and present the second alert or continue with some other operation.
I'm using the following snippet of code to make a tweet in my iOS 5 application :
- (IBAction)postToTwitterClicked:(id)sender
{
if ([TWTweetComposeViewController canSendTweet])
{
TWTweetComposeViewController *tweetSheet = [[TWTweetComposeViewController alloc]init];
[tweetSheet setInitialText:#"Some sample message here"];
[tweetSheet addURL:[NSURL URLWithString:#"http://myURL"]];
[self presentModalViewController:tweetSheet animated:YES];
}
else
{
UIAlertView *av = [[UIAlertView alloc] initWithTitle:#"Unable to tweet"
message:#"Please ensure that you have at least one twitter account setup and have internet connectivity. You can setup a twitter account in the iOS Settings > Twitter > login."
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[av show];
}
}
This works fine, but how do I know that the user did actually post a tweet, or if there was a problem?
Since this doesn't implement a delegate, there are no "onError" methods that I can override.
I want to know if the user did successfully post a tweet, so I can action some behaviour such as
Disable a button so they can't do it again
Notify them the post was successful and will show up in their feed shortly
There is no way in the iOS Twitter API that you can see that a Tweet actually was posted on the server. But you can analyze the TWTweetComposeViewControllerResult to see if the tweet was finished composing successfully or if the tweet was cancelled.
twitter.completionHandler = ^(TWTweetComposeViewControllerResult res) {
if (res == TWTweetComposeViewControllerResultDone) {
// Composed
} else if (res == TWTweetComposeViewControllerResultCancelled) {
// Cancelled
}
[self dismissModalViewControllerAnimated:YES];
};
Well, actually, you only can set a handler to call when the user is done composing the tweet: TWTweetComposeViewControllerCompletionHandler. This handler has a single parameter that indicates whether the user finished or cancelled composing the tweet.
You can try to send a tweet and make it fail to check the result code (luckily it's cancelled?).
Another alternative to achieve the desired behaviour is use another API.
Followup to Where is NSAlert.h in the iOS SDK?
Is there any way to get NSAlert runModal like behavior from a UIAlertView? Or from a UIActionSheet?
I'm planning on using only in debug builds so I'm not concerned with how it looks or if it uses undocumented functionality.
Edit:
NSAlert is part of the OS X SDK and is similar to MessageBox in Win32. It allows you to synchronously prompt the user for something. Here's an example:
NSAlert * myAlert=[[NSAlert alloc] init];
[myAlert setMessgeText:#"This is my alert"];
[myAlert addButtonWithTitle:#"button 1"];
[myAlert addButtonWithTitle:#"button 2"];
switch ([myAlert runModal]) {
case NSAlertFirstButtonReturn:
//handle first button
break;
case NSAlertSecondButtonReturn:
//handle second button
break;
}
runModal is a synchronous function, it shows the alert and waits for user response. Internally it is running a limited version of the message loop, but as far as the rest of my application is concerned, the world has stopped; no messages, no events, nothing.
Internally it is running a limited version of the message loop, but as far as the rest of my application is concerned, the world has stopped
Just do exactly what you described: throw up the alert, then run the event loop till the alert view gets dismissed. This code works:
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:#"O rlly?" message:nil delegate:nil
cancelButtonTitle:nil otherButtonTitles:#"OK", nil];
[alert show];
NSRunLoop *rl = [NSRunLoop currentRunLoop];
NSDate *d;
while ([alert isVisible]) {
d = [[NSDate alloc] init];
[rl runUntilDate:d];
[d release];
}
[alert release];
You'll have to write your own if you want this behavior. Careful, if you block the main queue for too long, your app will be watchdog'd.
UIAlertView gives you modal behavior, and will end up working the same way your custom class will. You might consider using a block-based wrapper that wraps up UIAlertView and allows you to setup blocks for the button action callbacks.