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")); }
