Can someone explain this for me?

  1. You’d be better off using composer.*, but whatever…

  2. Never destroy the display group given to you by storyboard (or by composer).  It is a managed group and you will kill storyboard/composer.

3.  If you need to destroy all the objects placed in that group you can do several things…

A. On create… make a group, add group to group supplied by storyboard/composer, then add objects to that group.  Later, delete that group and all children will be destroyed.

B. Iterate as follows to delete all children of any group (your code is incorrect, this is a correction)

-- Modified to clear ONE group i'm not sure what the bs was about, but -- you don't need to clear children groups of a group. Just kill the top group -- local function ClearStage(Grp) if( not Grp ) then return end while( Grp.numChildren \> 0 ) do display.remove(Grp[Grp.numChildren] ) end end

C. Many other better ways… but I’ve given two answers that will work so I think you should be solid.

Why not just silently log the error to a remote server and allow the app to continue?  Its a win-win, the app is not restarted and you get a log of the error to fix with a future update?

No one likes a reset!

Note: RG code is good but it assumes you’ve inserted everything into the scene group and you will have to reset transitions and timers relying on your display objects which will be a nightmare unless you are tracking them.

If you are worrying about performance the this will be faster

 for i = Grp.numChildren, 1, -1 do display.remove(Grp[i] ) end

Thanks Guys

Roaminggamer - your solution creates the same error that I was getting: -

ERROR: Runtime error

C:…\storyboard.lua:617: attempt to index local ‘bounds’ (a nil value)

stack traceback:

C:…\storyboard.lua:617: in function ‘saveSceneAndHide’

C:…\storyboard.lua:1627: in function <C:…\storyboard.lua:1560>

(tail call): ?

?: in function ‘?’

?: in function <?:190>

 

There is something about removing that first group or the children of the first group that storyboard does not like.

 

Sphere Game Studios

 

If I could avoid a reset I would.  The issue is that this is a business app which has lots of data transfers going on.  When you are using network.request Corona is not great at handling issues after those calls.  I have raised issues about it before.  The app can get pretty messed up if the request fails for connection issues and the user continues.

 

We are working on all areas of the app, but having a bullet proof reset would be useful.  The alternative is that the user clears the App from memory, but most of our users do not know how to do this…

 

Also, there are issues with just clearing the group.  Some of the widget objects (like newtableview) do not respond well if you clear the parent group without first clearing that object.  Spent many hours debugging only to find that one. That is why I walk the group tree to remove everything individually.

 

Anyway, I will keep plugging away at this…

OK, lets say you have 5 network requests that need to complete before continuing.  You could easily check each one and if all 5 pass then continue.  If any one fails then simply re-request it.  When all have passed testing set your completed flag to true and continue your processing.

My game is heavily networked (think hundreds of thousands of calls per day) and I get no issues with my logic.  You need to code for failure and only then code for suceeding.

FYI, I request a lot of JSON data.  A simple check that the last char is “}” will ensure you have a completed data send so you can then mark that data fragment as completed.  Once all fragments are completed you can continue.

I wish you luck!

both storyboard and composer create internal “stage” groups for their own use, and in both cases this is done at module scope - ie, it only happens once when the code is first required.  that’s your real issue, for a TRULY clean restart you need to unload all the lua modules too – certainly scene code, but not JUST scenes…

google for lua unrequire, it basically just involves wiping package.loaded[] for that module name.  in addition, you’ll have to consider your use (or abuse) of global variables that might be hanging on to references of things.

(also, neither storyboard nor composer make it easy to “exit all scenes” once you’ve started using them, which is absolutely necessary in order to get a clean unload of storyboard/composer AND the scene, they’re always wanting to hang onto some last-used “current scene”)

finally, if you’re able to get this far successfully, you want to manually run collectgarbage("collect’).  then, and only then, do you stand a chance of a truly clean reboot.  it would help if your main.lua looks no more complicated than this:

-- main.lua local app = require("app") app:boot()

then you could conceivably “compartmentalize” things enough to consider adding an app.reboot() function fe:

function app:reboot() self:destroy() self:boot() end

where “app.destroy” contains COMPLETE cleanup code.  the more “spaghetti” your code base might be, then the harder time you’d have of just trying to monkey-patch a reboot function on top of it - you instead need to DESIGN for it to make sure you do it completely, because there are runtime listeners to reset, etc, etc.

Keep in mind that composer.removeScene() by default unloads the scene. If I remember correctly storyboard’s removeScene() equivalent needs a parameter passed to unload the scene.

Rob

Thanks guys. The responses above have been really helpful.  I can’t recreate what I have seen on some IOS devices, but the issue appears to be when screens fail to load properly after network.requests fail - maybe due to a internet connection issue.

We are going to tighten up on our clean up code as best we can.  I think we also need to make sure that the network calls are as robust as they can be.

We have a few thousand users now and if it was a massive issue we would know about it, but it needs to be better than it is.  This app has over 300 lua files many of them making multiple network calls so there is a lot of opportunity for us to drop a ball.  

I think you are still destroying the managed group. You definitely don’t want to clear the stage as that contains the managed group.

What I want to remove ( and I don’t know how it is happening) is that we sometimes have objects that appear to be orphaned from the stage group.  e.g. they are created at a global level.

This only seems to happen on IOS and is pretty rare, but I still want to mop it up.

What I would like to do, in pseudo code, is something like this: -

Loop

     All objects that are not required for the stage remove and set to nil

End Loop

The reason I walk the tree above is there is a bug (or maybe it is by design) that if you try and remove a group object with a scrollview element such as scrollview or tableview etc, you get an error if you navigate back to the page.  It appears to leave the scrollview object in memory.  

Let’s be clear on terminology here.  When I say stage , I mean display.currentStage.  This is the top most group and first created group (it never changes) and if an object is not placed in a group by you it goes into ‘the stage’. 

Also note, It is impossible for an object to not be in a group.

I think you’re saying that objects meant to be in the scene group are not getting put there.  You need to track that down in your content creation code.  Destroying whole groups or iterating over the ‘stage’ and children to clean up is not a good solution.

The scroll view issue is a bug in the scrollview code.  You should download the entire widget repository, use the local copy you downloaded, track down the error, and prevent it.   I’m 90% sure you’re talking about an access bug where it complains a group or object is nil?  The solution is to modify that part of the widget code to check that the reference is valid.

If you can’t work this (fixing widgets) out, I’m open to doing a hit to help you with it.  This would be a level 2 hit and have to be done in two stages.  First, setting you up, second tracking down the bug you’re encountering.  You’ll have to supply logs during this part showing the error message.

One other note.  This won’t work:

Loop All objects that are not required for the stage remove and set to nil End Loop

If you have references to objects elsewhere in your code, the objects referred to will never be cleaned up.  You don’t set objects to nil.  You set the variable doing the referring to nil.  i.e. You clear the reference.

assuming you’re still attempting to fix what was in the original post… you can’t blindly delete storyboard/composer’s stage unless you un-require/re-require in order to recreate it before attempting to use it again.  but if un-require/re-require is too complicated, then one “simple” way around it might be to just tag composer’s stage somehow, so that you can later identify it as something that you shouldn’t mess with during your cleanup, fe:

local composer = require("composer") composer.stage.tag = "DON'T REMOVE ME OR COMPOSER WILL BARF"

(or same for storyboard, makes no difference)

In my personal experience, the code snippet shown here is the best way to clear objects from a group without getting any “bounds” type errors.

-dev

Ahhhhh!  I have found out the cause.  Although I am not sure what the solution is (certainly your ideas above will help).

So this is a business app which runs on an IPAD or Android Tablet in a shared environment.  When a user goes to use the App they must enter a pin.  If they do not sign out or if the IPAD goes to sleep or if the home screen is selected, I have to reset the app on suspension for security purposed.  I forgot about this!

This is what is causing the issue.  So I have this code in Main: -

local function onSystemEvent(event) if ((event.type == "applicationSuspend") and (\_G.CameraLoaded == nil)) then if tmid ~= nil then timer.cancel(tmid) tmid = nil end end if ((event.type == "applicationResume") and (\_G.CameraLoaded == nil)) then if \_G.PageLoaded ~= "firstPage" then local storyboard = require "storyboard" local LoadSplash = function() return storyboard.gotoScene( "splash", "fade", 100 ) end tmid = timer.performWithDelay( 1500, LoadSplash, 1 ) end end end Runtime:addEventListener("system", onSystemEvent)

The idea being when the app is resumed we navigate back to the Splash screen unless it is suspended on the Splash screen (or if the Camera is loaded which is fine as that is normal App usage).

On the splash screen I have this code: -

function scene:createScene( event ) group = self.view local prior\_scene = storyboard.getPrevious() storyboard.removeScenePurgeAll( prior\_scene )

However, when this is called after suspension it does not remove the scene as it is supposed to.  In fact on IOS it looks like it does nothing at all and the scene remains in memory causing chaos!

This is what is causing me the problems.

I will experiment with your ideas above, but any further advice would be really appreciated…

  1. You’re writing a new app and using storyboard?  Why?

  2. If you were using composer, you could find many examples, including one that shows on (of many techniques) to re-draw upon entering a scene and destroy upon leaving. See  #12 ==> 

https://github.com/roaminggamer/CoronaGeek/blob/master/Hangouts/composer_scene_manager/README.md

https://github.com/roaminggamer/CoronaGeek/raw/master/Hangouts/composer_scene_manager.zip

I’d be glad to set you up with a framework using either composer or (if you insist) storyboard as a HIT (probably a level 2). 

Thanks for all your responses.  I have found it!   I will document it here as other people may be as stupid as me and so this could help.

This only affects IOS.  Simulator in Windows or Android App are not affected.  Basically, when the App suspends the scene:destroy is not called.  Had a look through the Storyboard code here is this function: -

storyboard.purgeAll()

There must be a Composer equivalent.  Put this function in my suspend code: -

local function onSystemEvent(event) if ((event.type == "applicationSuspend") and (\_G.CameraLoaded == nil)) then if tmid ~= nil then timer.cancel(tmid) tmid = nil end storyboard.purgeAll() end if ((event.type == "applicationResume") and (\_G.CameraLoaded == nil)) then if \_G.PageLoaded ~= "firstPage" then local storyboard = require "storyboard" local LoadSplash = function() return storyboard.gotoScene( "splash", "fade", 2000 ) end tmid = timer.performWithDelay( 1500, LoadSplash, 1 ) end end end Runtime:addEventListener("system", onSystemEvent)

And now the Destroy is being called and everything seems OK again!

As with most things, simple solution in the end!

No, not totally fixed, but a lot better.  However, I can still reproduce the issue.  If you get chance have a look at this video: -

https://www.youtube.com/watch?v=L4G-Tvh5_PI

The first part just shows that the suspend is working when the screen loads.  At 38 seconds I suspend the screen again, but this time halfway through loading the page.

When the page is resumed we try and do a mop up with the above code, however you can see the page has crashed.  Well, we could just restart the device, but do you see “Getting Calendar…” text - that was being drawn at the crash.

Now, when I select restart I cannot get rid of that object…   sorry, you all must be bored of this now…

several related things are going on here…

first, neither storyboard/composer are well-behaved when you initiate multiple simultaneous scene transitions.  imagine the following:

-- in theory, 2000ms from now mainmenu will appear composer.gotoScene("mainmenu", { time=2000 }) -- simulate your suspend/resume half-way through transition timer.performWithDelay( 1000, function() composer.gotoScene("splash", { time=500 }) end)

Q:  what will actually happen??  at +1000ms?  at +1500ms?  at +2000ms? (left as an exercise for the reader to explore)

second, storyboard’s purgeAll() won’t remove the current scene.  so neither will it dispatch a destroy event to the current scene.  composer’s corresponding removeHidden() functions similarly (and with a more intuitive name).  So when you purgeAll() on suspend you are not actually destroying and un-require-ing the current scene (presumably your main screen) before reloading splash, so that’s probably where your lingering text object comes from.

both of these are factors for why your interrupted transition misbehaves

Thanks Dave, I have sorted I think.

I did some testing and what: -

storyboard.purgeAll()&nbsp;

does is to ensure that the scene:destroyScene is called which was not happening on an suspension on IOS.

The above video issue was caused by the 8 HTTPs calls that are going on during the suspension.  I made a tweak to our HTTP library that we wrote to interface with out cloud servers.  Basically if a suspension is detected a global variable is set to indicate that a suspension is happening.  When the app returns the HTTPs library does not call any of the call back functions - hence killing the calls backs.

The purgeAll is then initiated 1000 milliseconds later and we are restarting.  It seems rock solid now, even with suspensions during the calls.  We will continue to test, but really happy that the App is going to be a lot more robust when users close it during scene loads…