Problems with the Documents Directory

I am creating a system to save the state of my game. It works in the Corona simulator but if I build it for an IOS device or the XCode simulator (or possibly Android; I can’t build for Android right now.), the io.open function fails. This happens when reading and writing to the file. The file is in the documents directory in the project sandbox and the system.pathForFile() function returns true but when I use the path with io.open(), it returns false. Again, the code works on the Corona simulator so is there something else that I need to do to make it work on devices? 

Here is the code if it helps:

if roomsComplete ~= 0 then --do not save score of zero local path = system.pathForFile("highScoreData.txt", system.DocumentsDirectory) local file = io.open(path, "r") --open in read mode to get previous score local scores = {} for line in file:lines() do --insert all previous scores into table for sorting scores[#scores + 1] = line end scores[#scores + 1] = roomsComplete .. " " .. composer.getVariable("difficulty") --add new score to table along with difficulty file:close() table.sort(scores, function(x, y) --sort new score into correct pos in table return tonumber(x:match("%d+")) \> tonumber(y:match("%d+")) end) file = io.open(path, "w") --open in write mode to rewrite scores for i = 1, #scores do --loop through score file:write(scores[i] .. "\n") --write score end file:close() end composer.gotoScene("lose", "fade", 500) end

Thank you in advance for any help you can give.

You should always test the value returned by io.open() to make sure you get a file handle and not nil.

I did test it and it returns nil. That is the problem. system.pathForFile returns the file path though so I don’t think it is the problem.

I fixed it (I think)! Here is the new code:

This is executed in scene.create()

--initialize local path = system.pathForFile("highScoreData.txt", system.DocumentsDirectory) --load high scores from file local file = io.open(path, "r") if file then --only execute if the file exists for line in file:lines() do --loop through high scores if line:match("%a+") == composer.getVariable("difficulty") then --if the score's difficulty matches the current difficulty highScore = tonumber(line:match("%d+"))--use that score break end end file:close() else --otherwise create a new file DEBUG.text = "load failed" file = io.open(path, "w") file:write("0 easy\n") file:write("0 medium\n") file:write("0 hard\n") file:close() end

this is executed after the player loses:

if roomsComplete ~= 0 then --do not save score of zero local path = system.pathForFile("highScoreData.txt", system.DocumentsDirectory) --load high scores from file --[[if not path then return nil end]] local file = io.open(path, "r") --open in read mode to get previous score local scores = {} if file then DEBUG.text = "opened" for line in file:lines() do --insert all previous scores into table for sorting scores[#scores + 1] = line end scores[#scores + 1] = roomsComplete .. " " .. composer.getVariable("difficulty") --add new score to table along with difficulty table.sort(scores, function(x, y) --sort new score into correct pos in table return tonumber(x:match("%d+")) \> tonumber(y:match("%d+")) end) file:close() else DEBUG.text = "open failed" end file = io.open(path, "w") --open in write mode to rewrite scores if not file then DEBUG.text = "save failed" else DEBUG.text = "saved" for i = 1, #scores do --loop through score file:write(scores[i] .. "\n") --write score end file:close() end end

The method I used was to create the save file on the first launch rather than having the file pre-created in the build.

You should always test the value returned by io.open() to make sure you get a file handle and not nil.

I did test it and it returns nil. That is the problem. system.pathForFile returns the file path though so I don’t think it is the problem.

I fixed it (I think)! Here is the new code:

This is executed in scene.create()

--initialize local path = system.pathForFile("highScoreData.txt", system.DocumentsDirectory) --load high scores from file local file = io.open(path, "r") if file then --only execute if the file exists for line in file:lines() do --loop through high scores if line:match("%a+") == composer.getVariable("difficulty") then --if the score's difficulty matches the current difficulty highScore = tonumber(line:match("%d+"))--use that score break end end file:close() else --otherwise create a new file DEBUG.text = "load failed" file = io.open(path, "w") file:write("0 easy\n") file:write("0 medium\n") file:write("0 hard\n") file:close() end

this is executed after the player loses:

if roomsComplete ~= 0 then --do not save score of zero local path = system.pathForFile("highScoreData.txt", system.DocumentsDirectory) --load high scores from file --[[if not path then return nil end]] local file = io.open(path, "r") --open in read mode to get previous score local scores = {} if file then DEBUG.text = "opened" for line in file:lines() do --insert all previous scores into table for sorting scores[#scores + 1] = line end scores[#scores + 1] = roomsComplete .. " " .. composer.getVariable("difficulty") --add new score to table along with difficulty table.sort(scores, function(x, y) --sort new score into correct pos in table return tonumber(x:match("%d+")) \> tonumber(y:match("%d+")) end) file:close() else DEBUG.text = "open failed" end file = io.open(path, "w") --open in write mode to rewrite scores if not file then DEBUG.text = "save failed" else DEBUG.text = "saved" for i = 1, #scores do --loop through score file:write(scores[i] .. "\n") --write score end file:close() end end

The method I used was to create the save file on the first launch rather than having the file pre-created in the build.