0

I have a segue that I do like this:

[self performSegueWithIdentifier:@"PlanToBusiness" sender:self];

it works on the iPhone but not on the iPad. What I do is make a remote call to a server, and when the server response comes back correctly, I try to take the user to the new page with that segue.

It works on the iPhone simulator, but not the iPad (the identifying string is the same for both) and on the iPad it crashes with this error:

bool _WebTryThreadLock(bool), 0x68f9cb0: Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread. Crashing now...
1   WebThreadLock
2   -[UITextRangeImpl isEmpty]
3   -[UITextRange(UITextSelectionAdditions) _isCaret]
4   -[UITextSelectionView setCaretBlinks:]
5   -[UIKeyboardImpl setCaretBlinks:]
6   -[UIKeyboardImpl setDelegate:force:]
7   -[UIKeyboardImpl setDelegate:]
8   -[UIPeripheralHost(UIKitInternal) _reloadInputViewsForResponder:]
9   -[UINavigationController navigationTransitionView:didStartTransition:]
10  -[UINavigationTransitionView transition:fromView:toView:]
11  -[UINavigationTransitionView transition:toView:]
12  -[UINavigationController _startTransition:fromViewController:toViewController:]
13  -[UINavigationController _startDeferredTransitionIfNeeded]
14  -[UINavigationController pushViewController:transition:forceImmediate:]
15  -[UINavigationController pushViewController:animated:]
16  -[UIStoryboardPushSegue perform]
17  -[UIStoryboardSegueTemplate perform:]
18  -[UIViewController performSegueWithIdentifier:sender:]
19  __41-[PlanBusinessController submitBusiness:]_block_invoke_0
20  __block_global_0
21  -[NSBlockOperation main]
22  -[__NSOperationInternal start]
23  -[NSOperation start]
24  __block_global_6
25  _dispatch_call_block_and_release
26  _dispatch_worker_thread2
27  _pthread_wqthread
28  start_wqthread

I am also not too used to reading Obective-C stack traces so I am having a hard time pinpointing what the problem is.

Here is how I do this:

- (IBAction)submitBusiness:(id)sender 
{    
     // Make a url to send

     NSURL *url = [NSURL URLWithString:full_encoded_url_string];
     NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];

        // ***************
        // TODO: ok I dont really understand what this is
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        // **************

        [NSURLConnection sendAsynchronousRequest:urlRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
         {   
                         // I took out a lot of the logic code that is not needed

                         [self performSegueWithIdentifier:@"PlanToBusiness" sender:self];
         }];
    }
}
GeekedOut
  • 16,905
  • 37
  • 107
  • 185
  • How are you handling the remote call to the server? If it is an asynchronous call, and you are trying to segue in the thread that is calling the web service rather than the main thread, it could be causing this problem. Could you perhaps post a bit more code, where the request goes off and the full function where the segue is performed? – Paul O. Aug 17 '12 at 16:51
  • @Paul yes, I am doing an Asynchronous call and trying to segue from the return code. What is the right way to do this so that this error does not happen? ...posting code now... – GeekedOut Aug 17 '12 at 16:53
  • @Paul I just added some of the code that I use...I took out a lot of error checking and other logic, but the gist of what I do is there. – GeekedOut Aug 17 '12 at 17:00

1 Answers1

2

When methods that touch the UI, such when performing a segue, it is important to make sure you are calling that code from the main thread. The easiest way to do that is to make a method like this:

- (void)performPlanToBusinessSegueOnMainThread
{
    [self performSegueWithIdentifier:@"PlanToBusiness" sender:self];
}

Then, you can call this method in your finished block like this:

[NSURLConnection sendAsynchronousRequest:urlRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
 {   
     // I took out a lot of the logic code that is not needed

     [self performSelectorOnMainThread:@selector(performPlanToBusinessSegueOnMainThread) 
                            withObject:nil 
                         waitUntilDone:YES];
 }];

It is a little circuitous, but it should fix your exception. It would be better if we could just call performSegueWithIdentifier in the performSelectorOnMainThread method, but it has too many arguments.

Paul O.
  • 1,166
  • 8
  • 17
  • smart! I see what you are doing. So I should put the call to the function at the very very end of the code that comes back from the server, right? That way I am ensured that this thread stopped running? – GeekedOut Aug 17 '12 at 17:05
  • As long as you keep the `waituUntilDone` argument set to `Yes`, you should be able to put it anywhere you want and it will run pretty much the same as if it was not being called from a different thread at all (Except for the fact that it will hopefully not throw an exception!) It just causes the web thread to wait until the `performSelectorOnMainThread` function returns until it goes to the next line. – Paul O. Aug 17 '12 at 17:07
  • Using GCD will save you from creating new method. http://stackoverflow.com/questions/5662360/gcd-to-perform-task-in-main-thread – tia Aug 17 '12 at 17:09