Platform: I'm using macOS for the Simulator, however the actual crash only happens on device that I've seen! (Rev A, though I assume that's probably not relevant here.)
This was happening during SDK v2.6.x and I haven't tested it since but to my eyes, the gridview lib hasn't changed so hopefully this is still relevant. I'm just very late reporting this.
Expected: Removing a sprite that contains a gridview doesn't crash
Actual: It crashes intermittently with the following stack trace ![]()
CoreLibs/ui/gridview.lua:131: attempt to index a nil value (local 'gridView')
stack traceback:
CoreLibs/ui/gridview.lua:131: in field
'updateCallback'
CoreLibs/timer.lua:265: in local
'updateActiveTimer'
CoreLibs/timer.lua:351: in field 'updateTimers'
main.lua:236: in upvalue 'gameLoop'
main.lua:244: in function <main.lua:243>
Here's the bad news: I can't offer a small reproduction because I was never able to get it to happen reliably in the context of my game. I would have to play for ~20-30 minutes each time and there was no obvious pattern to when the crash would occur.
The (maybe?) slightly good news: I was able to fix it by patching CoreLibs/ui/gridview.lua. However, that's bad and I don't want to be doing that (for many obvious reasons)!
First, a couple details of the setup. To start, I have a class, FloatingPanelMenu, that extends gfx.sprite and ultimately wraps gridview up. The goal is to abstract all the drawing, input, etc. for common menus and reuse it quite often. It works well enough. The (whole) class is a bit of a monstrosity so I'm not sure it'll be as useful upfront but I can revisit that if necessary. The gist of the structure is this:
FloatingPanelMenu = class("FloatingPanelMenu").extends(gfx.sprite)
function FloatingPanelMenu:init(menuOptions, otherArgs, etc)
-- ...
self.gridviewImage = gfx.image.new(panelWidth, panelHeight)
FloatingPanelMenu.super.init(self, self.gridviewImage)
-- ...
local gridview = playdate.ui.gridview.new(0, baseCellHeight)
-- ...
function self:update()
-- handle gridview drawing, etc.
end
end
and my main loop:
local gameLoop = function()
DELTA_TIME = getETime()
resetETime()
-- ...
gfx.sprite.update()
playdate.timer.updateTimers()
blnkr.updateAll()
end
playdate.update = gameLoop
I tried quite a few things to adjust my FloatingPanelMenu class and nothing seemed to do the trick. So onto what "fixes" it (note the conditionals added inside each callback):
-- CoreLibs/ui/gridview.lua:128
gridViewForTimer[timerX] = o
timerX.updateCallback = function(timer)
local gridView = gridViewForTimer[timer]
if gridView then -- HACK: Only execute next lines if gridView still exists
gridView.scrollPositionX = timer.value
gridView.needsDisplay = true
end
end
timerX.timerEndedCallback = function(timer)
local gridView = gridViewForTimer[timer]
if gridView then -- HACK: Only execute next lines if gridView still exists
gridView.scrollPositionX = timer.endValue
gridView._isScrolling = false
gridView.needsDisplay = true
end
end
local timerY = playdate.timer.new(250)
timerY.discardOnCompletion = false
timerY.easingFunction = easingFunctions.outCubic
timerY:pause()
gridViewForTimer[timerY] = o
timerY.updateCallback = function(timer)
local gridView = gridViewForTimer[timer]
if gridView then -- HACK: Only execute next lines if gridView still exists
gridView.scrollPositionY = timer.value
gridView.needsDisplay = true
end
end
timerY.timerEndedCallback = function(timer)
local gridView = gridViewForTimer[timer]
if gridView then -- HACK: Only execute next lines if gridView still exists
gridView.scrollPositionY = timer.endValue
gridView._isScrolling = false
gridView.needsDisplay = true
end
end
As best I can tell, depending on how things get cleaned up/garbage collected/??? when removing the sprite (and thus the instance of gridview), these timers still get called but gridViewForTimer no longer contains a reference to the actual gridview.
All in all, I could be doing something very wrong/bad and it's nothing to do with the SDK! I also realize that without repro steps/a repro example, this could be impossible to investigate. I thought I'd post it anyway just in case something stood out since the hack to gridview internals 'fixes' it
Thanks so much in advance for taking a peek!