Here's the problem up front: I have a UIAlertController that has a textfield. I want to save the content of that textfield as an NSString when the user touches a "Confirm" button in the alert. When the Confirm action block is executed, however, the alert is nil (presumably already dismissed and deallocated at that point), and thus so is its textfield, meaning I cannot save the textfield's text.
I am using a series of UIAlertControllers to allow a user to create a passcode for my app, such that any time the app comes to the foreground, the user is prompted for the code before the app can be used.
I created a category of UIAlertController with several convenience methods that return preconfigured alerts that I need to use. Here's one of them:
+ (UIAlertController*)passcodeCreationAlertWithConfirmBehavior:(void(^)())confirmBlock andCancelBehavior:(void(^)())cancelBlock {
UIAlertController *passcodeCreationAlert = [UIAlertController alertControllerWithTitle:@"Enter a passcode"
message:nil
preferredStyle:UIAlertControllerStyleAlert];
[passcodeCreationAlert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.keyboardType = UIKeyboardTypeNumberPad;
}];
UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel"
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {
if (cancelBlock) {
cancelBlock();
}
}];
UIAlertAction* confirmAction = [UIAlertAction actionWithTitle:@"Confirm"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (confirmBlock) {
confirmBlock();
}
}];
[passcodeCreationAlert addAction:cancelAction];
[passcodeCreationAlert addAction:confirmAction];
passcodeCreationAlert.preferredAction = confirmAction;
confirmAction.enabled = NO;
return passcodeCreationAlert;
}
This method returns a UIAlertController that allows the user to enter their desired passcode into a textfield. When I call this method in my view controller, I pass blocks as parameters which are used as the UIAlertAction handlers:
- (void)presentCreatePasscodeAlert {
UIAlertController *alert = [UIAlertController passcodeCreationAlertWithConfirmBehavior:^{
firstPasscode = alert.textFields[0].text;
[self presentConfirmPasscodeAlert];
} andCancelBehavior:^{
[self presentEnablePasscodeAlert];
}];
alert.textFields[0].delegate = self;
[self presentViewController:alert animated:YES completion:nil];
}
To reiterate the problem now that there is more context: When the action block is entered at the line:
firstPasscode = alert.textFields[0].text;
the alert is nil, and so is its textfield, meaning I cannot save the textfield's text.
In a separate project, however, I tried getting the same functionality without using the category and custom convenience methods, and that works as desired:
- (void) createPassword {
UIAlertController *createPasswordAlert = [UIAlertController alertControllerWithTitle:@"Enter a password"
message:nil
preferredStyle:UIAlertControllerStyleAlert];
__weak ViewController *weakSelf = self;
[createPasswordAlert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.delegate = weakSelf;
textField.keyboardType = UIKeyboardTypeNumberPad;
}];
UIAlertAction* confirmAction = [UIAlertAction actionWithTitle:@"Confirm"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
self.password = createPasswordAlert.textFields[0].text;
[self confirmPassword];
}];
UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[self promptPasswordCreation];
}];
[createPasswordAlert addAction:confirmAction];
[createPasswordAlert addAction:cancelAction];
confirmAction.enabled = NO;
[self presentViewController:createPasswordAlert animated:YES completion:nil];
}
Sure enough, in the above code the alert exists when the Confirm block is entered, and I can save the text just fine.
Have I done something screwy by passing blocks as parameters to my convenience methods?