Hans Hans - 1 year ago 48
iOS Question

TFileStream.Create fails on iOS with certain device languages

When I set my iOS device to e.g. Vietnamese, then the following code fails sometimes:

lFilePath: String
lFilePath := TPath.GetTempPath + '/MyDBfile.db';
lFileStream := TFileStream.Create(lFilePath, fmOpenReadWrite or fmShareExclusive or fmCreate);

The TFileStream.Create call raises an assert: "EFCreateError: Cannot create file "/private/var/mobile/Containers/Data/Application/{containerID}/tmp/MyDBfile.db"
No such file or directory"
The assert is never raised with Western European languages, only when the device is set to certain languages (including Vietnamese).

I traced the create code down to this line of the
function in

FileHandle := Integer(__open(M.AsAnsi(FileName, CP_UTF8).ToPointer,
O_RDWR or O_CREAT or O_TRUNC or Exclusive[(Mode and $0004) shr 2], Rights));

FileHandle is -1 when the assert is raised.

What can be wrong?

PS: In my attempt to find out what happens, I added a

lFilePath := TPath.GetTempPath + '/MyDBfile.db';
if Fileexists(lFilePath) then
lFileStream := TFileStream.Create(lFilePath, fmOpenReadWrite or fmShareExclusive or fmCreate);

Now, in the situations where the code fails, I have the following strange findings:
In XCode, which can show the Container for the App, it shows the file
in the Container, i.e. the file does exist (the file is only created by the quoted code, so it was created one of the times the code succeeded). However, at the same time
returns false.

The file is a SQLite file that is later opened by
and shortly after closed by
. Could SQLite maybe put the file in a state where Fileexists returns false? (the state remains after restarting the app)

Answer Source

The problem is the current implementation of TPath.GetTempPath. It uses the general Posix approach ExpandFileName('~/tmp/'). However, the Apple recommended method is to use NSTemporaryDirectory.

Both methods return the path to the tmp directory, but NSTemporaryDirectory also creates the folder if it does not exist - that seems to be the difference. The strange thing is that the Delphi implementation only fails with certain iOS device languages (including Vietnamese). I did not investigate that further, but the solution for now is to simply replace TPath.GetTempPath with this Delphi code: