I'm used to working with SQL database, I'm new to Realm and, so far, I'm really impressed by the ease of use of this new mobile database.
But there is something that I really don't undestand: how to handle error throwing?
Take this simple example:
I want to store in a Realm DB some market stocks.
Each stock has a "symbol" as a unique identifier: APPL for Apple Inc, TSLA for Tesla Motors Inc, etc.
I believe it would make sense to declare these symbols as primary keys, since it's not possible to have multiple times the same symbol in the database
When user clicks on a symbol (among a list of symbols), this symbol is saved in db.
In this Raywenderlich tutorial, it is said: "To simplify the code required in this tutorial, you’ll be used try! when calling Realm methods that throw an error. In your own code, you should really be using try and do / catch to catch errors and handle them appropriately."
So according to the following pattern:
do {
try realm.write {
realm.add(symbol)
}
}
catch let error as NSError {
print("Something went wrong: \(error.localizedDescription)")
}
So far it makes sense.
And if a user clicks on a symbol that is already in the database, I get (very logically) an error:
*** Terminating app due to uncaught exception 'RLMException', reason: 'Can't set primary key property 'symbol' to existing value 'APPL'.'
The problem is that this error is not catched during run time: I've got a crash instead.
My question is not about how to avoid such a crash, I understand of course that it's easy to avoid it by doing a simple testing before writing anything in the database :)
My question is how do we do in Realm to catch potential writing errors?
Am I doing something wrong?
do/try/catch in Swift catch Swift Errors, which are an entirely distinct thing from Objective-C exceptions. Realm follows Foundation's pattern for error reporting: API misuse errors throw exceptions which are not intended to be caught (and can't be caught in Swift), and recoverable errors throw Swift errors (or use NSError out parameters in Objective-C).
Adding an object with a duplicate primary key is considered API misuse, so it's a fatal error as the route for "handling" it is to fix the bug in your code. An example of a recoverable error which would produce a Swift error that would be caught by catch is running out of disk space while trying to save the new data.
There are two distinct types of error: programmer error, and expected errors. Realm handles these two types of errors differently.
Programmer error covers cases such as accessing objects from incorrect threads, out-of-bounds collection access, attempting to insert an object with a duplicate primary key, and so forth. Realm responds to these programmer errors by throwing an Objective-C exception. These exceptions are not intended to be caught and handled as they're indicative of a bug in a Realm user's code.
Expected errors cover things like files not existing, permission errors, failures to write to disk. These are things that can happen even with correctly-written code due to factors outside of the programs control. These are exposed as NSErrors and can be caught from Swift using do / try / catch. Note that Realm's Objective-C API has some convenience methods that don't expose the NSError. In these cases an expected error will throw an exception as there's no other avenue for Realm to communicate the failure. Rather than attempting to catch and handle exceptions raised by these convenience methods, you should use the variant of the API that returns an NSError instead.
Hi, the error line said "to existing value", don't you try to write more than one time the same primary key with 'APPL' ?
you need to update it, not override.
if you try to "add" again the same value, it's gonna stop you.
(sorry for bad english x) )
Related
While decrypting I get the error : The operation couldnot be performed RNCryptorError 1
I dont understand what I am doing wrong. Here is my block of code
For anyone who might search here: this is a duplicate of RNCryptor#174, and you may want to read there as well.
Please just post code into the question rather than a screenshot. I can't compile a screenshot, and they're very hard to read.
Error 1 is an HMAC error. Either your data is corrupted or your password is incorrect.
Note that NSException never makes sense in Swift. Switch can't catch them. They only make sense in ObjC if you're going to crash the program shortly after. They're not memory-safe in ObjC. You meant to use Swift's throw and ErrorType, which are unrelated to raise or NSException.
I have a few doubts regarding exception handling in iPhone. Here are they:
Suppose I have a chain of methods which are being called one after the other, that is, method A calls method B, which in turn calls method C, which calls method D. Which is the best place to put my try-catch block (is it method A or B or C or D or all of them). Also, I need to display an alert to user that an exception has occured and then, I want to log this exception to my server. So, if I am writing my try - catch block in all of this methods and if an exception occurs in method D; then I think the alert will be displayed 4 times and the web service for logging will also be called 4 times (till control reaches to catch block of method A). So, should I just use #throw; in catch block of method B, C and D and write my logic in catch block of method A (top level method) or should I avoid writing try - catch at all in methods B,C and D.
I need some sort of error code from the exception (because my web service needs parameters error-code and description). Is it possible to convert an exception to error or will I need to hard-code this code?
I had read somewhere about NSSetUncaughtExceptionHandler. And I think, if I can set this handler (in appDidFinishLaunching method of app delegate) and in the handler method, if I show to user some alert and call the web service; then I need not write try - catch block in each of my methods, in each of my classes. Am I right??
If an exception has occured and I have written either a try - catch block or NSSetUncaughtExceptionHandler, then will my app continue running or it will not respond to any of the user events. (I am sure it will handle the crash. What I want to know is whether it will hang)
Someone please enlighten me on this EXCEPTION TOPIC.
0) Avoid exceptions in Cocoa. They are generally non-recoverable. You might catch them for your own error reporting, but it's generally unsafe to assume you can recover from them.
1) If you need to catch, catch it immediately. Don't write your own throws -- instead, convert it to something like an NSError and pass that around. NSError can contain all the information you need to display or send an error code as well as a localized message.
2) You cannot convert an NSException into an NSError (directly) because an NSException does not have all the properties an NSError has - it is a different data representation. For one, an error code is not available. Two, the description is not localized. The best you can do is to create an error code and domain, then use the properties you need from the NSException and store that in an NSError. This could look something like the following:
// error checking omitted
extern NSString* const MONExceptionHandlerDomain;
extern const int MONNSExceptionEncounteredErrorCode;
NSError * NewNSErrorFromException(NSException * exc) {
NSMutableDictionary * info = [NSMutableDictionary dictionary];
[info setValue:exc.name forKey:#"MONExceptionName"];
[info setValue:exc.reason forKey:#"MONExceptionReason"];
[info setValue:exc.callStackReturnAddresses forKey:#"MONExceptionCallStackReturnAddresses"];
[info setValue:exc.callStackSymbols forKey:#"MONExceptionCallStackSymbols"];
[info setValue:exc.userInfo forKey:#"MONExceptionUserInfo"];
return [[NSError alloc] initWithDomain:MONExceptionHandlerDomain code:MONNSExceptionEncounteredErrorCode userInfo:info];
}
#catch (NSException * exc) {
NSError * err = NewNSErrorFromException(exc);
...
}
If the APIs you use throw exceptions you are expected to catch and recover from (e.g. not truly exceptional cases), then yes, you could catch and attempt to continue. Unfortunately, anybody that writes exceptions in Cocoa with the intent that you will catch them probably does not understand the issues well enough to implement a solid unwind implementation (e.g. even if it produces leaks, it's not solid).
3) That's really not the time or place to display an alert. If you install a top level exception handler (via NSSetUncaughtExceptionHandler) - You should simply log a message -- then the exception handler will abort. Your app is in an unstable state -- continuing is worse than aborting. You may want to send these custom messages home, it's best to do so at the next launch of your app.
4) In the majority of cases, your app is in an unstable state and you should not continue. But, to actually answer it for those corner cases: "Yes, you can recover and continue when you catch, but you should only attempt to recover and continue when the throwing API states that recovery is supported. If the problem is beyond your control, and the problem is not exceptional (e.g. file not found), and the vendor really expects you to continue, then I would have to assume that they expect you to continue, even though it really isn't (100% safe).". Do not attempt to recover/continue from within your top level exception handler (the program will abort after it returns). If you want to be very fancy and present that immediately on OSX, another process would be best. If you are calling through a pure C++ interface, then the unwinding is well defined, and the need to catch is necessary - do continue if it is recoverable. Exceptions in C++ can be recoverable and well defined - they are also used quite extensively (including less than exceptional conditions).
(IMO...) Exceptions in ObjC should not have been introduced, and any method that throws from system or third party libraries should be deprecated. They don't unwind well, or in a well defined manner. As well, unwinding flows against normal Cocoa program flow. That means that touching any objc object's memory/relations that was in mutation at the time of the throw and which lies between the throw and the catch is as good as undefined behaviour. Problem is - you have no idea what that memory is (in most cases, and within reasonable maintenance time). C++ exceptions are well defined, and they unwind correctly (e.g. destructors are called) - but trying to continue in an ObjC context is ignoring any consequences of undefined behavior. IMO, they should only exist for ObjC++ (because C++ requires them).
In an ideal world, your ObjC programs and the libraries you use would not use exceptions (at all). Since you use libraries that do throw (including Cocoa), install a top level exception handler only if you need some special information about the error. Where the API mandates that you could expect an exception thrown due to circumstances beyond your control and you are expected to recover, then write a catch but immediately convert that logic to normal program flow (e.g. NSError) - you never need to write your own throw. -[NSArray objectAtIndex: and "object does not respond to selector" are examples of programmer errors - they should not be caught, but the program should be corrected.
Many of the Caché API methods return a %Status object which indicates if this is an error. The thing is, when it's an unknown error I don't know how to handle (like a network failure) what I really want to do is "throw" the error so my code stops what it's doing and the error gets caught by some higher level error handler (and/or the built-in %ETN error log).
I could use ztrap like:
s status = someObject.someMethod()
ztrap:$$$ISERR(status)
But that doesn't report much detail (unlike, say, .NET where I can throw an exception all the way to to the top of the stack) and I'm wondering if there are any better ways to do this.
Take a look at the Class Reference for %Exception.StatusException. You can create an exception from your status and throw it to whatever error trap is active at the time (so the flow of control would be the same as your ZTRAP example), like this
set sc = someobj.MethodReturningStatus()
if $$$ISERR(sc) {
set exception = ##class(%Exception.StatusException).CreateFromStatus(sc)
throw exception
}
However, in order to recover the exception information inside the error trap code that catches this exception, the error trap must have been established with try/catch. The older error handlers, $ztrap and $etrap, do not provide you with the exception object and you will only see that you have a <NOCATCH> error as the $ZERROR value. Even in that case, the flow of control will work as you want it to, but without try/catch, you would be no better off than you are with ZTRAP
These are two different error mechanisms and can't be combined in this way. ztrap and %ETN are for Cache level errors (the angle bracket errors like <UNDEFINED>). %Status objects are for application level errors (including errors that occurred through the use of the Cache Class Library) and you can choose how you want to handle them yourself. It's not really meaningful to handle a bad %Status through the Cache error mechanism because no Cache error has occurred.
Generally what most people do is something akin to:
d:$$$ISERR(status) $$$SomeMacroRelevantToMyAppThatWillHandleThisStatus(status)
It is possible to create your own domain with your own whole host of %Status codes with attendant %msg values for your application. Your app might have tried to connect to an FTP server and had a bad password, but that doesn't throw a <DISCONNECT> and there is no reason to investigate the stack, just an application level error that needs to be handled, possibly by asking the user to enter a new password.
It might seem odd that there are these two parallel error mechanisms, but they are describing two different types of errors. Think of one of them being "platform" level errors, and the other as "application level errors"
Edit: One thing I forgot, try DecomposeStatus^%apiOBJ(status) or ##class(%Status).LogicalToOdbc(status) to convert the status object to a human readable string. Also, if you're doing command line debugging or just want to print the readable form to the principal device, you can use $system.OBJ.DisplayError(status).
I am not able to understand the places where an error handling or where an exception handling should be used. I assume this, if it is an existing framework class there are delegate methods which will facilitate the programmer to send an error object reference and handle the error after that. Exception handling is for cases where an operation of a programmer using some framework classes throws an error and i cannot get an fix on the error object's reference.
Is this assumption valid ? or how should i understand them ?
You should use exceptions for errors that would never appear if the programmer would have checked the parameters to the method that throws the exception. E.g. divide by 0 or the well known "out of bounds"-exception you get from NSArrays.
NSErrors are for errors that the programmer could do nothing about. E.g. parsing a plist file. It would be a waste of resources if the program would check if the file is a valid plist before it tries to read its content. For the validity check the program must parse the whole file. And parsing a file to report that it is valid so you can parse it again would be a total waste. So the method returns a NSError (or just nil, which tells you that something went wrong) if the file can't be parsed.
The parsing for validity is the "programmer should have checked the parameters" part. It's not applicable for this type of errors, so you don't throw a exception.
In theory you could replace the out of bounds exception with a return nil. But this would lead to very bad programming.
Apple says:
Important: In many environments, use of exceptions is fairly commonplace. For example, you might throw an exception to signal that a routine could not execute normally—such as when a file is missing or data could not be parsed correctly. Exceptions are resource-intensive in Objective-C. You should not use exceptions for general flow-control, or simply to signify errors. Instead you should use the return value of a method or function to indicate that an error has occurred, and provide information about the problem in an error object.
I think you are absolutely right with your assumption for Errors and for it framework provide a set of methods (UIWebView error handling ), But your assumption for Exception partially right because the exception only occurred if we do something wrong which is not allowed by the framework and can be fixed. (for example accessing a member from an array beyond its limit).
and will result in application crash.
Apologies if this question has already been answered somewhere else, but I could not find any decisive answer when searching on it:
I'm wondering when try-catch blocks are to be used in objective-c iPhone applications. Apple's "Introduction to the Objective-C Programming Language" state that exceptions are resource intensive and that one should "not use exceptions for general flow-control, or simply to signify errors." From reading a few related questions here I also gather that people are not often using this method in practice.
So I guess the question is: what are the situations when it's appropriate to use try-catch blocks when developing for iPhone/Objective-C and when should they absolutely NOT be used?
For example, I could use them to catch beyond bounds and other exceptions when working with objects in arrays. I have a method which performs are few tasks with objects that are passed on in a number of arrays. The method returns nil if an error has occurred and a try-catch block could help me catch exceptions. However, I could of course simply write little if-tests here and there to ensure that I, for instance, never try to access an index beyond an arrays bounds. What would you do in this situation?
Thanks a lot!
It is only appropriate to use #try/#catch to deal with unrecoverable errors. It is never appropriate to use #throw/#try/#catch to do control-flow like operations.
In particular, it would not be appropriate to use for catching out-of-bounds exceptions unless your goal is to catch them and somehow report the error, then -- typically -- crash or, at the least, warn the user that your app is in an inconsistent state and may lose data.
Behavior of any exception thrown through system framework code is undefined.
Your if-test to do bounds checking is a far more appropriate solution.
#bbum's answer is absolutely correct (and he would know the answer better than most). To elaborate a bit...
In Cocoa, you should generally avoid using exceptions (#try/#catch[/#finally]) for flow control. As you mention, exceptions carry an unusually large cost (compared to run-times such as JVM or the CLR optimized for exception use). Furthermore, most of the Cocoa frameworks are not exception safe. Thus, throwing an exception through Cocoa framework code is dangerous and will likely cause odd, difficult to diagnose, and catastrophic (think possible data loss) bugs in your app.
Instead of using exceptions, Cocoa code uses NSError to signal error conditions that are recoverable within the application. Exceptions are used to signal conditions from which your application cannot recover. Thus a UI component requesting an out-of-bounds position in a model array could be signaled with an error (presented to the user with a reason their request could not be completed) while attempting to access an out-of-bounds position given an index that you think should be valid signals an exceptional condition where you app is in an inconsistent state and should probably die as soon as possible before it can do more damage.
NSParameterAssert, for example signals with an NSException when an assertion fails.
So when should you use exceptions or #try/#catch? If you're using a C/C++ library that makes use of exceptions, you should catch those exceptions before they can get thrown back through Cocoa code. Similarly, if you are serious about consistency of state within your application, you should raise an exception as soon as you detect that your state is inconsistent (and unrecoverable).