When I set my iOS device to e.g. Vietnamese, then the following code fails sometimes:
var
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 FileCreate
function in System.SysUtils
:
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 Fileexists
call:
lFilePath := TPath.GetTempPath + '/MyDBfile.db';
if Fileexists(lFilePath) then
System.Sysutils.DeleteFile(lFilePath);
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 tmp/MyDBfile.db
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 Fileexists
returns false.
The file is a SQLite file that is later opened by sqlite3_open_v2
and shortly after closed by sqlite3_close
. Could SQLite maybe put the file in a state where Fileexists returns false? (the state remains after restarting the app)
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:
NSStrToStr(TNSString.Wrap(NSTemporaryDirectory))