How can I call error recovery?

I think this is not the appropriate forum. Nevertheless, I try to get an answer!


For error recovery in a COCOA program I defined my user dictionary as follows:


id userInfoDict = @{

NSLocalizedDescriptionKey : @"Fehler!",

NSRecoveryAttempterErrorKey : self,

NSLocalizedRecoverySuggestionErrorKey : description,

NSLocalizedRecoveryOptionsErrorKey : @[@"zurücksetzen", @"korrigieren"]

};


The resulting error message contains the localized description as well as the RecoverySuggestion.


But instead of the requested recovery options buttons I get the (system) buttons 'ok' and 'discard changes'.


And 'attemptRecoveryFromError: ……' is not called.


Can somebody give me a hint what my mistakge might be?


Hans

Answered by QuinceyMorris in 242904022

The good news is that I can get and run your project just fine.


The bad news is that it doesn't attempt recovery, and AFAICT this is intentional. It looks to me like the binding validation mechanism suppresses the recovery attempt. (It may be that at the time validateKey is called, the array controller is not in a state where it can fully hand control over to the recovery attempter, but that's just a guess.)


The bindings validation does call "presentError:modalForWindow:…" as expected, but the call is wrapped in a private method that handles this kind of "discard changes?" alert specially. If I just invoke "presentError:" directly, for example when a new path is added, the recovery options are presented correctly.


My suggestions:


— Use a TSI (tech support incident) to get input from Apple as to whether there's any way around the current behavior, but you should expect "no" as a possible answer.


— Alternatively, don't use the validate<key> mechanism at all. It's actually kind of awful as a UI, because it means you can't tab out of the field temporarily, and fix the error later. This is likely to make users angry.


Instead, allow the user to enter incorrect values, and validate them "later" (that is, just before you need to rely on them being correct). You might then invoke "presentError:…" yourself, and the recovery attempter will get control, if that's what you want.


Sorry for the bad news. I don't think you're doing anything wrong, and I think you are right to expect it to attempt recovery, but … it is what it is. Bug report or TSI, if you want to pursue this.

I just tried calling "presentError…" using an error built with your userInfo dictionary. At my first attempt, I got a log message saying that the recovery attempter didn't implement the "attemptRecoveryFromError…" method. When I added that, I got a dialog showing your two recovery buttons ("zurücksetzen" and "korrigieren"), and when I clicked one of them, the "attemptRecoveryFromError…" method was invoked. So, everything seemed to be working fine.


Perhaps you're trying to use something other than "presentError…"? You didn't show the rest of your code for causing the error to be displayed.

I use the following validation:


- (BOOL) validateKey: (id*) input error: (id*) outError {


and on 'NO' answer I set


*outError = [NSError errorWithDomain: @"Fehler!" code: 0 userInfo: userInfoDict];


For recovery I call


- (BOOL) attemptRecoveryFromError: (NSError *) error optionIndex: (NSUInteger) recoveryOptionIndex {


According to the documentation this should work - but it doesn't!

I think there are 2 possibilities:


1. That might be the wrong "attemptRecovery" method. It's used for an application-modal error window. If validation is using an error sheet (I can't remember if it does, and don't have a suitable test app handy to check), then you need to use "attemptRecoveryFromError:optionIndex:delegate:didRecoverSelector:contextInfo:". You could try providing both.


2. The mechanism that invokes "validateKey" might not be using "presentError", which is the API that handles recovery, AFAIK.


Is this a validation on a binding? Core Data? Something custom?

'ad 1: I used 'attemptRecovery' according to a suggestion in the documentation. In another program I used it for a dictionary - key + one value -, and it works!


ad 2: I am not familiar with the internals.


ad 'binding': the tableView is bound through an NSArrayController to the model. Each item in the array has 3 properties, one of them is 'key' in 'validateKey:…'.

When you see the error dialog (OK/Discard Changes), is it a freestanding window, or a sheet on an existing window?

It's a freestanding window!

Sorry, wrong answer! It's a sheet.

Whew! Better answer. In that case, you need to provide "attemptRecoveryFromError:optionIndex:delegate:didRecoverSelector:contextInfo:".

Sorry, that too does not work.


By the way, the problem should - as far as I see it - be not the error recovery, but the error reporting ('validate: ……). Here I define the buttons which do not appear.


Hans

Can you make a test project that shows the incorrect behavior?

I prepared a downstripped version of my program.

But how can I submit it?

github? dropbox? tinyupload?

I never used 'dropbox'.


I installed it and tried to copy the test application into dropbox. This is the link to it:


https://www.dropbox.com/sh/7k2naohxflmzp33/AACFFr2T3r3CRLiqFmAmRUYma?dl=0


I hope it is readable for you!!!


Hans

Accepted Answer

The good news is that I can get and run your project just fine.


The bad news is that it doesn't attempt recovery, and AFAICT this is intentional. It looks to me like the binding validation mechanism suppresses the recovery attempt. (It may be that at the time validateKey is called, the array controller is not in a state where it can fully hand control over to the recovery attempter, but that's just a guess.)


The bindings validation does call "presentError:modalForWindow:…" as expected, but the call is wrapped in a private method that handles this kind of "discard changes?" alert specially. If I just invoke "presentError:" directly, for example when a new path is added, the recovery options are presented correctly.


My suggestions:


— Use a TSI (tech support incident) to get input from Apple as to whether there's any way around the current behavior, but you should expect "no" as a possible answer.


— Alternatively, don't use the validate<key> mechanism at all. It's actually kind of awful as a UI, because it means you can't tab out of the field temporarily, and fix the error later. This is likely to make users angry.


Instead, allow the user to enter incorrect values, and validate them "later" (that is, just before you need to rely on them being correct). You might then invoke "presentError:…" yourself, and the recovery attempter will get control, if that's what you want.


Sorry for the bad news. I don't think you're doing anything wrong, and I think you are right to expect it to attempt recovery, but … it is what it is. Bug report or TSI, if you want to pursue this.

Many thanks!


I will try just such a 'circumvention'!

How can I call error recovery?
 
 
Q