Cannot write file on iPhone

Godot Version

4.2.2

Question

My game needs to read a json file, modify its content, and overwrite it with the new content. Both reading and writing work perfectly within Godot, but when I export the game and load it into my iPhone only reading works, while writing fails because the FileAccess.open() function returns a null object.

Here is a simplified version of the code (affected by the same problem) that just reads the json and rewrites it as it is:

var path = “res://my_file.json”
var file = FileAccess.open(path, FileAccess.READ)
var content = file.get_as_text()
file.close()
json = JSON.new()
parsed_content = json.parse_string(content)
file = FileAccess.open(path, FileAccess.WRITE)
file.store_string(json.stringify(parsed_content))
file.close()

As I mentioned above, the problem is that FileAccess.open(path, FileAccess.WRITE) returns a null object on my iPhone, while FileAccess.open(path, FileAccess.READ) works perfectly fine (I can even see the content of the file without problems). Both instructions work fine within Godot.

When I run the game on my iPhone via xcode I get this error message:

USER SCRIPT ERROR: Attempt to call function ‘store_string’ in base ‘null instance’ on a null instance.

The error is at line:
file = FileAccess.open(path, FileAccess.WRITE)

Is anyone experiencing the same problem?

Not sure if this is the cause of the error, but you cannot write to res:// on an installed game, but you can read. If you need to write, you need to write to user://

2 Likes

Thank you GameDevGeezer, but unfortunately no luck.

I put my file in the user folder, which I found in Project/Open User Data Folder, and again it works perfectly within Godot. However, when I export the game for iOS and run it on my iPhone via xcode I get the same problem as before. Actually even worse, because now I cannot even read the file. It still say that the FileAccess.open() function is returning a null object. I am addinng user://my_file.json and *.json to Filters to export non-resource files/folders during the exporting process.

That is strange. I am not sure what’s happening, maybe it a scope issue?

There’s also a FileAccess.get_error() and FileAccess.get_open_error() function that may provide additional detail via a return code you can reference in the docs.

Godot will not export “user://” files. You need to copy it to the iPhone, or include a default in “res://my_file.json” which you would open if they do not have a “user://” version.

# determine if user's file exists or use default
var user_file := "user://my_file.json"
if not FileAccess.file_exists(user_file):
    user_file = "res://my_file_default.json" # use default

# read and parse the file
var file := FileAccess.open(user_file, FileAccess.READ)
var content = file.get_as_text()
file.close()
json = JSON.new()
parsed_content = json.parse_string(content)

# write to user file
file = FileAccess.open("user://my_file.json", FileAccess.WRITE)
file.store_string(json.stringify(parsed_content))
file.close()

Yes, I did exactly that and it works. Thank you gertkeno, and thank you GameDevGeezer for setting me on the right path.

As you can see I’m new to Godot and I’m trying to understand how it works. So, if I understand correctly, you cannot export a file directly into user://, but the game itself can write it. I guess this has to do with security? Therefore, the standard method is to export the file into res://, and then tell the game to copy it into user:// the first time you play. The file will then remain inside user:// ever after. Am I correct?

Thank you both again!

Manu

2 Likes

That’s correct… and thanks to @gertkeno for adding info I forgot to post.

You do have to copy the file from res:// to user:// the first time. The original file will remain in res:// but will not be touched after the initial run (or if the user resets, if you have that option in the game).

Check to see if the file exists in user:// and if it does not, copy it over from res://

Cheers!