why do I get "wait_fences: failed to receive reply" for this code? - iphone

why do I get "wait_fences: failed to receive reply" for this code? Is it the way I'm using notification to communicate back to the main thread?
#import "ViewController.h"
#implementation ViewController
#synthesize alert;
#pragma mark - Background Thread Test Methods
- (void) ConfigTasksForBackground:(id)sender{
NSLog(#"ConfigTasksForBackground - Starting");
[NSThread sleepForTimeInterval:6];
[[NSNotificationCenter defaultCenter] postNotificationName:#"ModelChanged" object:self];
NSLog(#"ConfigTasksForBackground - Ending");
}
#pragma mark - Callbacks
- (void) ModelChangedHandler:(NSNotification *) notification {
if ([[notification name] isEqualToString:#"ModelChanged"]) {
NSLog(#"ModelChangedHandler");
[self.alert dismissWithClickedButtonIndex:0 animated:false];
}
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(ModelChangedHandler:)
name:#"ModelChanged"
object:nil];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
self.alert = [[[UIAlertView alloc] initWithTitle:#"Title"
message:#"viewDidAppear"
delegate:nil
cancelButtonTitle:nil
otherButtonTitles:nil] autorelease];
[alert show];
[self performSelectorInBackground:#selector(ConfigTasksForBackground:) withObject:nil];
}
#end
Output is:
2011-11-07 15:15:42.730 test_background[6876:13603] ConfigTasksForBackground - Starting
2011-11-07 15:15:48.734 test_background[6876:13603] ModelChangedHandler
2011-11-07 15:15:49.236 test_background[6876:13603] ConfigTasksForBackground - Ending
wait_fences: failed to receive reply: 10004003

There is an obvious problem here. You are posting the notification from the background thread (which is fine) which means the notification handler ModelChangedHandler is being called on the background thread. The handler is then dismissing an alert view which must be done on the main thread. Try changing your code to:
- (void) ModelChangedHandler:(NSNotification *) notification {
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:#selector(ModelChangedHandler:) withObject:notification waitUntilDone:NO];
}
else if ([[notification name] isEqualToString:#"ModelChanged"]) {
NSLog(#"ModelChangedHandler");
[self.alert dismissWithClickedButtonIndex:0 animated:false];
}
}
EDIT: was typing too fast, changed answer to reflect the correct UI objects.

Here's how to get rid of the wait_fences error. Change the line where you dismiss the alertView to use animation as follows:
[self.alert dismissWithClickedButtonIndex:0 animated:YES];
I think wait_fences has something to do with view animation states with alert views but it's hard to know for sure. I do think this should eliminate the error msg though. My other answer doesn't directly get rid of the error but I still recommend it. UI actions should be done on the main thread.

Related

Hide/Unhide IBOutlet(UIButton) when a IAP is complete

Alright, so I've been toying around with this for a couple days now and I haven't gotten this to work, looking to you for help!
Basically I want to "Unlock" a feature once an IAP is done. I've got the IAP code to work, but I want to change the button "sendMail" ('disabled' in Interface Builder) so that the user can interact with it.
//InputViewController.h
#import "IAPStore.h"
#interface InputViewController : UIViewController <MFMailComposeViewControllerDelegate, UIAlertViewDelegate>
#property(strong,nonatomic)IBOutlet UIButton *sendMail;
-(void)enableMail;
....
#end
//InputViewController.m
#import "InputViewController.h"
#import "IAPStore.h"
-(void)enableMail
{
[_sendMail setEnabled:YES];
NSLog(#"Unlocking Button");
}
//IAPStore.h
#import "InputViewController.h"
#interface IAPHelper : NSObject <UIAlertViewDelegate>
-(void)purchaseComplete;
...
#end
//IAPStore.m
#import "InputViewController.h"
-(void)purchaseComplete
{
UIAlertView *purchased = [[UIAlertView alloc]initWithTitle:#"In-App Purchase" message:#"Purchase complete! Thank you!" delegate:nil
cancelButtonTitle:#"OK" otherButtonTitles:nil];
GROWInputViewController *viewController = [[GROWInputViewController alloc] init];
[viewController enableMail];
[purchased show];
NSLog(#"button enabled");
}
So it prints out too the log but nothing is changed on the other view controller, but nothing is changed, any idea to what I'm doing wrong?
You could use NSNotificationCenter
In the viewDidLoad: method of InputViewController.m add this line of code:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(enableMail) name:#"purchaseCompleteNotification" object:nil];
And in the purchaseComplete method of IAPStore.m, replace this:
GROWInputViewController *viewController = [[GROWInputViewController alloc] init];
[viewController enableMail];
with this:
[[NSNotificationCenter defaultCenter] postNotificationName:#"purchaseCompleteNotification" object:nil];
This will cause a notification to be posted when the purchase is complete. Meanwhile, InputViewController has an 'observer' that is set to call your 'enableMail' method when that notification is posted.
Also, you'll want to add this method to your InputViewController.m, so that he is removed as an observer when deallocated.
-(void)dealloc{
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"purchaseCompleteNotification" object:nil];
}

alertView didDismissWithButtonIndex never called

Please excuse me if something is not post right... first time posting.
I have seen a few questions simular to this but none with the same problem. I am running IOS 6.1 and Xcode 4.6. The problem is that didDismiss is never called, only willDismiss. My code is below along with the log output. Any ideas?
#import "MenkLabUIAlertTestViewController.h"
#interface MenkLabUIAlertTestViewController ()
#end
#implementation MenkLabUIAlertTestViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (IBAction)test:(id)sender {
UIAlertView *av = [[UIAlertView alloc] initWithTitle:#"Encrypting File(s)" message:#"Please wait..." delegate:self cancelButtonTitle:nil otherButtonTitles:nil, nil];
// UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
[av show];
[av dismissWithClickedButtonIndex:-1 animated:YES];
}
- (void) alertView:(UIAlertView *)alertView willDismissWithButtonIndex:(NSInteger)buttonIndex {
NSLog(#"willDISMIS");
}
- (void) alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
NSLog(#"didDISMIS");
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Log output:
2013-07-08 17:27:04.055 testUIAlertView[10534:11303] willDISMIS
This is just a test app, however, it is the exact same problem that exists in my current application.
Thanks in advanced. Been racking my head on this all day!
I think this an artifact of the fact that you are showing, then immediately dismissing the alert view in the same method -- you would never actually do this in a real app. If you create a property for the alert view, and then do the test like below, it works fine:
- (IBAction)test:(id)sender {
self.av = [[UIAlertView alloc] initWithTitle:#"Encrypting File(s)" message:#"Please wait..." delegate:self cancelButtonTitle:nil otherButtonTitles:nil, nil];
[self.av show];
[self performSelector:#selector(dismissAlertView) withObject:nil afterDelay:1];
}
-(void)dismissAlertView {
[self.av dismissWithClickedButtonIndex:-1 animated:YES];
}
- (void) alertView:(UIAlertView *)alertView willDismissWithButtonIndex:(NSInteger)buttonIndex {
NSLog(#"willDISMIS");
}
- (void) alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
NSLog(#"didDISMIS");
}
I had faced a similar issue, as a workaround we added a selector Method which runs after some delay which will instead trigger the dismissal of alert view. I am not sure why it does not work if we ask the alert to to dismiss immediately after it is shown. Hope it helps.
I ran into this problem too. For me it was related to trying to programatically dismiss it with a button index on -1. We ended up going down a different path in the end for other reasons. However, There is a cancel button index on the actionsheet that you can try calling it with.
I ran into this problem once. For me the problem was caused by a collision between animations. The didDismiss selector is called when the animation ends. If another animation is started between willDismiss and didDismiss then in some rare circumstances the didDismiss doesn't have to be called.
Also note that it never works well if you try to dismiss the alert before it is fully displayed.
I have added. That solves my problem.
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 1)
{
}
}

How to refresh UIWebView when app comes to foreground?

I would like to refresh a UIWebView whenever my app comes to the foreground. All I really have in my ViewController.m is a method that checks for internet access (hasInternet) and viewDidLoad.
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize webview;
-(BOOL)hasInternet{
Reachability *reach = [Reachability reachabilityWithHostName:#"www.google.com"];
NetworkStatus internetStats = [reach currentReachabilityStatus];
if (internetStats == NotReachable) {
UIAlertView *alertOne = [[UIAlertView alloc] initWithTitle:#"You're not connected to the internet." message:#"Please connect to the internet and restart the app." delegate:self cancelButtonTitle:#"Dismiss" otherButtonTitles:nil];
[alertOne show];
}
return YES;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self hasInternet];
[webView loadRequest: [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://warm-chamber-7399.herokuapp.com/"]] ];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Any advice on how to enable this functionality? Does it go in AppDelegate or do I create another method within ViewController.m?
You should register a UIApplicationWillEnterForegroundNotification in your ViewController's viewDidLoad method and whenever app comes back from background you can do whatever you want to do in the method registered for notification. ViewController's viewWillAppear or viewDidAppear won't be called when app comes back from background to foreground.
-(void)viewDidLoad{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(doYourStuff)
name:UIApplicationWillEnterForegroundNotification object:nil];
}
-(void)doYourStuff{
[webview reload];
}
Don't forget to unregister the notification you are registered for.
-(void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
Note if you register your viewController for UIApplicationDidBecomeActiveNotification then your method would be called everytime your app becomes active, It would not be appropriate to register for this notification.
Register for the UIApplicationDidBecomeActiveNotification or the UIApplicationWillEnterForegroundNotification.

How to avoid UIAlertView duplicate show from Notfications

I have a selector to show UIAlertView asking user if want to retry upload images after NotificationCenter post a notification with observename.
[[NSNotificationCenter defaultCenter] postNotificationName:kNOTIFICATION_PHOTOS_UPLOAD_RETRY object:nil];
But because of the notification received more than one, so will show as many as notifications received. Is there a best practice to show alert view only once?
Yeah, you can do something like:
#interface MyClass
{
UIAlertView *_myAlertView;
}
#end
#implementation MyClass
...
- (void)myNotificationSelector:(NSNotification *)notification
{
if (!_myAlertView) {
_myAlertView = [[UIAlertView alloc] init ...]
_myAlertView.delegate = self;
[_myAlertView show];
}
}
...
#end
and in the UIAlertViewDelegate handlers, just release and set _myAlertView to NO.

Frustrating UIWebView Delegate Crash issue

I've created an ARC Application that run's perfect. It's got a UINavigationController that I use to push through the views and everything runs fine.
I'm converting the Application to iPad and i've decided to show one of the views as a popover. (I don't like UIPopoverController so i've created my own basic popup). It's added to the view as follows..
MyViewController *hotelinf = [[MyViewController alloc]
initWithNibName:#"MyViewController_iPad" bundle:nil];
[self.view addSubview:hotelinf.view];
The view is added as a subview fine. The view i'm adding contains a UIWebView that has the usual delegate methods in it, but when the view tries to access one of the delegates it simply crashes.
*** -[MyViewController respondsToSelector:]: message sent to deallocated instance 0x7917b90
Specifically, it crashes on this line..
[self.webView loadHTMLString:stringResponse baseURL:nil];
I've displayed views (and UINavigationControllers) as subViews many of times without any issues, although none of them included a UIWebView delegate. I'm guessing I have to set the delegate of my UIViewController istance but i'm not sure how. It's also worth noting that if I push the view in my existing UINavigationController it calls and loads the HTML fine, which surely means it has nothing to do with the code of the view itself.
Any help would be greatly appreciated! Here is the code (in addition to above that shows the controller)..
.h
#interface MyViewController : UIViewController <UIWebViewDelegate, UIActionSheetDelegate> {
//Unrelated IBOutlets
}
#property (nonatomic, strong) UIWebView *webView;
#end
.m
#implementation MyViewController
#synthesize webView;
- (void)viewDidLoad
{
[super viewDidLoad];
self.webView = [[UIWebView alloc]initWithFrame:CGRectMake(317,283,393,354)];
self.webView.delegate = self;
[self.view addSubview:self.webView];
[NSThread detachNewThreadSelector:#selector(getHTMLString) toTarget:self withObject:nil];
}
-(void)getHTMLString {
#autoreleasepool {
//Download a valid HTML String
[self performSelectorOnMainThread:#selector(loadHTML) withObject:nil waitUntilDone:NO];
}
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
self.webView = nil;
}
-(void)loadHTML {
self.webView.opaque = NO;
self.webView.backgroundColor = [UIColor clearColor];
if ([stringResponse isEqualToString:#""]) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"Could not connect to XXXXX.com. Please verify you are connected to a working 3G/WIFI Network." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
} else {
//it crashes here only when loaded as a subview - the first access to the delegate
[self.webView loadHTMLString:stringResponse baseURL:nil];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
[self stopIndicator];
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
if (error.code == NSURLErrorCancelled) return; // this is Error -999
[self stopIndicator];
// report the error inside the webview
NSString* errorString = [NSString stringWithFormat:
#"<html><center><font size=+10 color='black' face='Helvetica'>An error occurred:<br>%#</font></center></html>",
error.localizedDescription];
[self.webView loadHTMLString:errorString baseURL:nil];
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:#"Cannot load URL."
message:#"You have a connection failure. Please verify you are connected to a WIFI or 3G enabled Network."
delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[alert show];
}
#end
The issue has nothing to do with the UIWebView, rather with your controller class. Indeed,
MyViewController *hotelinf = [[MyViewController alloc]
initWithNibName:#"MyViewController_iPad" bundle:nil];
[self.view addSubview:hotelinf.view];
You are allocating the controller and assigning it to a local variable; then you add the controller's view as subview to your current view. Doing that, that view is retained, but what happens to the controller object itself? Are you releasing it? Or it leaks (since it is assigned to a local variable)?
This possibly explains why when later the respondsToSelector method is called, the controller has already been deallocated...
A way to fix this is creating a new property or an ivar in your main controller class and store MyViewController in there. Don't forget to release it in dealloc.
I would also suggest another thing. In:
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
self.webView = nil;
}
set the webView delegate to nil before releasing the view:
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
self.webView.delegate = nil;
self.webView = nil;
}
And I would also possibly review the reason why you release the webView in viewDidDisappear. On the other hand you allocate it in viewDidLoad. This asymmetry is dangerous, since whenever the main view disappears (for any reason) the webView will be removed and when the view reappears, it is not there anymore.
Better add all the delegate methods. You havent added the first two. Most probably, your code is crashing when message webViewDidStartLoad is sent
– webView:shouldStartLoadWithRequest:navigationType:
– webViewDidStartLoad:
– webViewDidFinishLoad:
– webView:didFailLoadWithError: