The debugger can help you verify that your code is working as you expect and it can help you find errors. Once you've found the source of errors, you want to make sure you handle them properly.
Exceptions are a type of error that occur when something unexpected happens. These errors will crash your application if you do not handle them in some way. The act of causing an exception to occur is called raising an exception.
All exception are subclasses of the RuntimeException class.
When an exception is encountered in your code, you can choose to have the Debugger displayed at the line causing the exception. To do this, select Project > Break On Exceptions in the menu so that it has a checkmark next to it.
Since exceptions are for unexpected behavior, if you are able to avoid an error by preventing the exception from occurring then it is more efficient to do so. One reason is that when testing, the Debugger is displayed for any event that is raised if you have "Break On Exceptions" enabled in the Project menu. For example, with a Dictionary you use the Value method to fetch a value based on a key. If the key is not found you'll get a KeyNotFoundException. But you can avoid that by using either the Dictionary (to apply a default if the key is not found) or the Dictionary methods. So if you do expect situations where a key may not be found then you can account for it with these methods. And since that is not something unexpected, relying on the KeyNotFoundException is unnecessary.
The most common type of exception that occurs is a NilObjectException. This exception occurs when you attempt to use an object but don't have an instance of it. If a NilObjectException occurs in your built app, then a dialog appears:
If you are running your app in debug mode from within the Xojo IDE, then first the Debugger appears showing you the line of code that caused the exception and you can see the exception in the Variables pane of the Debugger. When you Resume, the above dialog will be displayed if your app does not handle the exception. If your app handles the exception then the exception handler runs and your code continues.
The simplest example of how you can get a NilObjectException is forgetting to use New to get an instance before you try to use a property or call a method on it:
Var d As Dictionary d.Value("ID") = "Test" // NilObjectException
The above code raises a NilObjectException because you did not use New to create a Dictionary instance (or an object). The correct way to write this code is with the New command:
Var d As New Dictionary d.Value("ID") = "Test" // NilObjectException
Another place to check for this is in the return value of methods that return an instance. Some methods return Nil in certain situations, such as FolderItem.ShowOpenFileDialog which returns Nil if the user clicks Cancel:
Var f As FolderItem f = FolderItem.ShowOpenFileDialog("") If f.Exists Then // NilObjectException // Do something End If
To prevent these types of errors, which is preferred, you should always check if the value is Nil before you attempt to access it:
Var f As FolderItem f = FolderItem.ShowOpenFileDialog("") If f <> Nil Then If f.Exists Then // Do something End If End If<
Sometimes you may find that an exception is a normal part of processing. What you want to do in these situations is catch the exception so that you can deal with it appropriately rather than letting your app crash. The Try command is used for this purpose.
For example, loading an XML file can raise an XMLException if the file is not valid XML. You can check for this exception by using a Try..Catch block:
Var xmlFile As FolderItem xmlFile = FolderItem.ShowOpenFileDialog("") If xmlFile <> Nil Then Try Var xml As New XmlDocument xml.LoadXml(xmlFile) Catch e As XmlException MessageBox("Not an XML file!") Return End Try End If // Process the XML
If no XMLException is raised then the code in after the End Try section runs. If an XMLException is raised, then the code in the Try block stops running and the code in the Catch block is run, which in this case displays a message and allows the app to continue running.
When you are running in debug mode, the Debugger appears when the exception is raised, even if you have code to handle the exception (when Project > Break On Exceptions is turned on). Choose Resume to have your app continue running.
If you'd like to avoid the Debugger from appearing in specific situations you can temporarily turn this behavior off by using a Pragma:
#Pragma BreakOnExceptions Off Try // your code Catch e As NilObjectException // handle exception End Try #Pragma BreakOnExceptions Default // Restore setting from Project menu
The Exception command goes at the end of the method and is called if an exception is generated anywhere in the method. The preceding example could be written like this using the Exception command:
Var xmlFile As FolderItem xmlFile = FolderItem.ShowOpenFileDialog("") If xmlFile <> Nil Then Var xml As New XmlDocument xml.LoadXml(xmlFile) End If // Process the XML Exception e As XmlException MessageBox("Not an XML file!") Return
Although this is simpler, it is not as obvious what is occurring (especially if there is even more code in the method) and it is also far less flexible since it only works on the entire method. In most cases you should use Try...Catch.
There is a special event handler in the App object of your project that is called if an unhandled exception is not caught using Try...Catch or Exception.
The event has one parameter, error, which is the exception itself. You can put code in this event handler to display a message to the user, capture log information or anything else you want. Return True to hide the default unhandled exception error dialog and attempt to allow you application to continue, but this is not recommended because your app may no longer be stable depending on what caused the exception.
This code in App.UnhandledException displays a friendlier message to the user and then quits the application:
Var msg As String = "An error occurred. Please notify the author." MessageBox(msg) Quit Return True
You can also display the runtime stack so it can be used to help pinpoint the location of the problem so you can fix it:
Var msg As String = "An error occurred. Please notify the author. Stack: " MessageBox(msg + error.Stack.FromArray(EndOfLine)) Quit Return True
Remember, if this event handler has been called and you return False (the default) it means your entire web app is going to stop running. Return True to hide the default error dialog and prevent the web app from quitting.
iOS apps do not display an error dialog for unhandled exceptions; the app just terminates. You'll need to add your own code to App.UnhandledException to display unhandled exceptions.
Creating your own exceptions
Since RuntimeException is a class like any other, you can subclass it to create your own exceptions. This allows you to raise your own exceptions that you have defined.
If you create a RuntimeException subclass called InvalidXMLFileFormatException then you can raise it using this syntax:
If incorrectXmlFileFormat Then Raise New InvalidXMLFileFormatException End If