It’s Wednesday and time for another frequently asked questions (FAQs) session. Here are some FAQs about Corona run-time errors.
1. My app was working fine and now I’m seeing a pop-up saying an error was found and my app quits. What changed?
Starting with build 2013.1030 on Android, run-time errors now display a pop-up alerting you to the problem and displaying information about the error. Previously, run-time errors were only displayed in the “adb logcat” window and may have gone undetected. We added the pop-ups so we could deal with run-time errors that could occur after we removed the default Android permissions. Currently, the iOS builds don’t support this feature.
2. I don’t remember getting these errors before. Are these real errors?
Yes, if you are getting a run-time error, it’s because an exception occurred and your program failed to execute the code after the error. Your app may have been getting errors all along, but either you were not looking for them, they occurred only once in a while, or they only occurred on devices that you did not test.
Generally these errors occur because of nil values returned by an API, and the value is later used to do something. One example is trying to remove a Corona object when the object pointer is nil. Another is displaying a text string that is concatenating a string with a returned value that was nil. The general cause is a programming mistake where the variable/object is “out of scope” in the function that is trying to use it or not checking for a nil value after calling a function.
Here is one example:
local touchRect = display.newRoundedRect( 0, 50, 70, 30, 10 ) touchRect.x = display.contentCenterX local count1 = 0 local count2 = 0 local count3 = 0 function touchRect:touch( event ) if event.phase == "ended" then count1 = count1 + 1 count2 = count2 + 1 count3 = count3 + 1 text1.text = "Count1 = " .. count1 text2.text = "Count2 = " .. count2 -- << error here text3.text = "Count3 = " .. count3 end return true end text1 = display.newText( "Count1 = 0", 10, 100, native.systemFont, 20 ) local text2 = display.newText( "Count2 = 0", 10, 140, native.systemFont, 20 ) text3 = display.newText( "Count3 = 0", 10, 170, native.systemFont, 20 ) touchRect:addEventListener( "touch" )
Tapping on the white rectangle causes all three counts to increment by one and display the updated counts. There is a bug that causes an error when "text2.text" is set. The error: "attempt to index global 'text2' (a nil value)", is because text2 is defined as a local variable and was defined after the touch function. It's out of scope to the function and is nil.
If you run the above code in the simulator and tap on the rectangle, you will see the first counter increments but not the other two. The reason is the chunk of code aborts right after trying to set text2.text -- the remaining code never executes. You can keep tapping the rectangle and the first counter keeps incrementing. To the person using the app, it looks like the program is running fine if they didn't know the other two counters should increment too.
The changes we made to the Android code will detect this run-time error and display a pop-up with the file and line number of the error. You may think that your app was running fine before, but if your code has run-time errors, the portion of your code after the errors were not really executing. It should be noted that errors like this will be detected in all platforms and displayed on the console. You must have a console window open in order to see it it (Xcode for iOS or "adb logcat" for Android).
The above error can be easily fixed by either by defining text2 before the function definition or removing "local" where text2 is defined:
text2 = display.newText( "Count2 = 0", 10, 140, native.systemFont, 20 )
3. How do I keep the pop-up from happening when I release my app to Google Play?
Currently, there are two ways to avoid the pop-up: 1) find all bugs and fix them (as shown above), or 2) add a pcall statement to areas of your code that you know could cause problems.
We understand that it's not possible to find and kill all the bugs in an app so we are planning to add a run-time listener that will trap errors and allow the program to decide how to deal with them. If you build an app for the App Store and a user runs across an error, you could log the error and send it back to your servers for evaluation or simply ignore it. This feature is not available now, but should be in a future Daily Build soon.
4. Is there any way to trap the errors myself and not have it quit the app or display a pop-up to the user?
Besides waiting for a Daily Build with the new run-time error listener, you can wrap pieces of your code in a Lua pcall statement to trap errors. The pcall (protected call) can be used to call any function or method, with the call returning the execution status. It returns true if the call executed correctly, and false if it failed because of a run-time error. Since it traps the run-time error, it avoids the pop-up and doesn't quit the app. There is a slight performance overhead using pcall, so it should only be used where you need to deal with errors that may occur outside the control of the app (e.g., network calls).
The syntax of pcall is:
pcall( f [, ...] )
where f is the function to be called,
and ... is the arguments for the function.
For the sake of this exercise I wrapped setting text2 in a function (setText2) and called it using pcall. The results of the call is stored in status and displayed.
local touchRect = display.newRoundedRect( 0, 50, 70, 30, 10 ) touchRect.x = display.contentCenterX local count1 = 0 local count2 = 0 local count3 = 0 local function setText2( value ) text2.text = "Count2 = " .. value end function touchRect:touch( event ) if event.phase == "ended" then count1 = count1 + 1 count2 = count2 + 1 count3 = count3 + 1 text1.text = "Count1 = " .. count1 local status = pcall( setText2, count2 ) print( "pcall status is ", status ) text3.text = "Count3 = " .. count3 end return true end text1 = display.newText( "Count1 = 0", 10, 100, native.systemFont, 20 ) local text2 = display.newText( "Count2 = 0", 10, 140, native.systemFont, 20 ) text3 = display.newText( "Count3 = 0", 10, 170, native.systemFont, 20 ) touchRect:addEventListener( "touch" )
When you run this modified code, the pcall status is false, indicating that a run-time error occurred. Since both counters 1 and 3 increment, this shows that the pcall trapped the run-time error and allowed the remaining code to execute.
The pcall is only useful where you expect your code could fail and need to guard against it or test for the failed condition.
5. Why is the error pop-up only in Android builds?
We made the change first in Android because we needed a way to deal with the errors that could occur when we removed the default permissions in the Android builds. We feel detecting and trapping errors is a useful thing for our developers, and plan to add this feature to iOS as well as the Mac and Windows simulator in an upcoming Daily Build.
In the current release and Daily builds, we do provide run-time error information that can help you find bugs in your apps. If you do a developer build, you will see the file, line number, and the type of error in the console. Release builds (for the App Store) will only show the type of error encountered.
As an added note, if you are using the Mac Simulator, be sure to start the Corona Terminal and not the Corona Simulator. The Corona Terminal loads a console window and then starts the Corona Simulator. The console window displays print, warning and error messages.
That's it for today's FAQs. I hope you enjoyed the post, and even learned a few things.