in order to not lock the ui iVersion runs it’s svn commands in a seperate threads. The promblem arises when one of thses threads needs to prompt for a value e.g. a username / password. Firstly UIKit does not provide a modal alert view and secondly UIKit should only be called from the mainThread.
First we create a new class which has a view which will be loaded from a nib, a thread condition to lock the calling thread and two NSString fields to store the result and the string of the button which was clicked.
@interface MessagePrompt : NSObject
{
UIView * view;
NSCondition * promptCondition;
NSString * clickedButton Text;
NSString * result;
{
@end
Our new prompt method can be called from the thread which needs to be locked, it will subsequently call UIKit on the main thread.
+(id)newPrompt:(MessagePrompt*)prompt
{
if ([NSThread currentThread] == [NSThread mainThread])
{
printf("Should not be called by Main thread\n");
return nil;
}
prompt.promptCondition = [[[NSCondition alloc] init] autorelease];
[prompt performSelectorOnMainThread:@selector(prompt:) withObject:view waitUntilDone:NO];
[prompt.promptCondition wait];
return prompt;
}
This method is called by +(id)NewPrompt and displays a prompt in our window. It also adds targets to each UIButton to receive user actions.
-(void)prompt:(UIView*)inView
{
NSArray * nib = [[NSBundle mainBundle] loadNibNamed:@"MyPromptView" owner:self options:nil];
inView = [nib objectAtIndex:0];
NSArray * subviews = [self.view subviews];
for (UIView * cview in subviews)
{
if ([cview isKindOfClass:[UIButton class]])
{
UIButton * button = (UIButton*)cview;
[button addTarget:self action:@selector(clicked:) forControlEvents:UIControlEventTouchDown];
}
}
[inView.superview addSubview:view];
}
This is called when a user clicks a button, it finds the value field and retreives and stores the value in self.message. The prompt condition is then signalled which causes the calling thread to continue running.
-(IBAction)clicked:(UIButton*)sender
{
self.clickedButton = sender.currentTitle;
UITextView * text = [iVersionAppDelegate findSubViewOfKind:self.view class:[UITextView class]];
if (text)
{
self.message = text.text;
}
[self.view removeFromSuperview];
self.view = nil;
[promptCondition signal];
}
@end
Usage
/* Called when svn requires a log message */
svn_error_t *
iversion_svn_cl__get_log_message(const char **log_msg,
const char **tmp_file,
const apr_array_header_t *commit_items,
void *baton,
apr_pool_t *pool)
{
CommitLogMessagePrompt * prompt = [CommitLogMessagePrompt newPrompt];
/* Clicked Continue */
if ([prompt.clickedButton isEqualToString:@"Continue"])
{
*log_msg = [prompt.message UTF8String];
return SVN_NO_ERROR;
}
return svn_error_create(SVN_ERR_CANCELLED, NULL, _("Delete Cancelled"));
}
