Xcode gotcha on file saving

This has cost me HOURS. I hope genuinely I can save you some time here if you ever get to the Xcode stage.

The long and short of it is only “Documents:” works as a format that will write and load files between Xcode launches of your app.

So for now we all should be using the “Documents:” format exclusively for file saving if we ever want to port our projects to Xcode.

This is the code that finally clarified it for me. Run it twice in Xcode to see the results. You’ll have to change the print statements to objc.log to see them in the Xcode console.

function launchNumber()
  local n = (readLocalData("PIN_lc") or 0) + 1
  saveLocalData("PIN_lc", n)
  return n
end

function textsFoundByDocuments()
  local a = readText("Documents:_pin_str.txt")
  return a and ("'" .. a .. "'") or "nil"
end

function textsFoundByAssetDocuments()
  local a = readText(asset.documents .. "_pin_key.txt")
  return a and ("'" .. a .. "'") or "nil"
end

function saveTextsBothWays()
  saveText("Documents:_pin_str.txt", "hello")
  saveText(asset.documents .. "_pin_key.txt", "hello")
  return "saved"
end

function testTextSaves()
  print("launch " .. launchNumber())
  print("texts found by 'Documents:': " .. textsFoundByDocuments())
  print("texts found by asset.documents: " .. textsFoundByAssetDocuments())
  print(saveTextsBothWays())
end

function resetTestState()
  saveLocalData("PIN_lc", 0)
  saveText("Documents:_pin_str.txt", nil)
  saveText(asset.documents .. "_pin_key.txt", nil)
  saveImage("Documents:_pin_str_img", nil)
  saveImage(asset.documents .. "_pin_key_img", nil)
  print(“reset done — next launch will be launch 1 with all nil")
end


function imagesFoundByDocuments()
  local a = nil; pcall(function() a = readImage("Documents:_pin_str_img") end)
  return a and ("image " .. a.width .. "x" .. a.height) or "nil"
end

function imagesFoundByAssetDocuments()
  local a = nil; pcall(function() a = readImage(asset.documents .. "_pin_key_img") end)
  return a and ("image " .. a.width .. "x" .. a.height) or "nil"
end

function saveImagesBothWays()
  local r = image(32,32); setContext(r); background(255,0,0,255); setContext()
  saveImage("Documents:_pin_str_img", r)
  saveImage(asset.documents .. "_pin_key_img", r)
  return "saved"
end

function testImageSaves()
  print("images found by 'Documents:': " .. imagesFoundByDocuments())
  print("images found by asset.documents: " .. imagesFoundByAssetDocuments())
  print(saveImagesBothWays())
end

Sorry this caught you out. It’s very subtle.

Basically, when your project is running in Codea, the file system looks like this:

Codea.app [READ ONLY]
 |_ Codea's assets and binary

Codea.app's Sandbox
 |_ Documents
     |_ YourProject.codea
         |_ Main.lua
         |_ File.png
     |_ OtherProject.codea
         |_ ...
 |_ Caches
 |_ Application Support
 |_ tmp

When you export your project to Xcode, you are building your own .app with its own sandbox, and that looks like this:

YourExportedApp.app [READ ONLY]
 |_ YourProject.codea
     |_ Main.lua
     |_ File.png

YourExportedApp.app's Sandbox
 |_ Documents
 |_ Caches
 |_ Application Support
 |_ tmp

So anything that lives in your project is effectively frozen - it’s read only as apps cannot modify themselves at runtime. They can only write into their sandboxes, which give you standard locations like documents / caches / etc. That’s why this issue exists. I think we could improve this, need to think on how.

That said, asset.documents should work in exported projects and I consider it a bug that it doesn’t. Will look at fixing it next.