can't open file for writing with FileHandle(forWritingTo: fileURL)

I'm trying to open a file for writing. I can get the URL but the FileHandle always comes back nil. Any idea why this won't work?


let fileURL = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false).appendingPathComponent("test.tag")
  
let file: FileHandle? = try FileHandle(forWritingTo: fileURL)


file always comes back as nil. I get this error:

Error Domain=NSCocoaErrorDomain Code=4 "The file “test.tag” doesn’t exist." UserInfo={NSFilePath=/var/mobile/Containers/Data/Application/0D688141-D9C2-4CD0-849F-A2CBC69E34CD/Documents/test.tag, NSUnderlyingError=0x1c465d760 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}


Isn't the FileHandle call supposed to create the file? How else do you do it? The only other create file I can find is one that creates a file when you already have the complete contents to provide at the start, and I don't.


Thanks!

Accepted Reply

Okay I got this working. It's either a roundabout, screwed up way or Apple really doesn't want us writing out files incrementally. Seems braindead, because there are MANY situations where you don't want to create an entire copy of your data-to-write in memory so that you can save it out in one call. I can't believe simply writing a few bytes to a file and reading them back is so complicated in Swift but here is the only way I've found if you're not going the NSArchiver route:


var value = someInt
var data = Data(buffer: UnsafeBufferPointer(start: &value, count: 1))
try data.write(to:fileURL)
       
       
let file: FileHandle? = FileHandle(forUpdatingAtPath: fileURL.path)
file?.seekToEndOfFile()
value = anotherInt
data = Data(buffer: UnsafeBufferPointer(start: &value, count: 1))
file?.write(data)
       
value = yetAnotherInt
data = Data(buffer: UnsafeBufferPointer(start: &value, count: 1))
file?.write(data)

value = aFourthInt
data = Data(buffer: UnsafeBufferPointer(start: &value, count: 1))
file?.write(data)


I couldn't get a FileHandle to write to until I actually used Data.write to create the file -- make sense? NO! After that, you cannot continue to use data.write, like any normal person would think, because apparently data.write does not append, it only overwrites at the beginning of the file. Surely there is some way to do this that I am missing?


THEN, to write more data, you can get FileHandle forUpdatingAtPath because you've now created the file by writing out one piece of data. Thereafter, you can use file.write(data) to write additional items to the file. BUT WAIT - make sure you call file.seekToEndOfFile() before every write, or it won't move past the last thing you wrote.


Sheez -- so complicated to do something so simple -- someone please illuminate me if there's an easier way. And I'm just talking about old-school writing bytes to files sequentially. I am not talking about writing all my data to a file at once, which is easy if you have all your data in one place. And not NSArchiver which is not practival sometimes.


However -- because this is so screwy I am going to go back and look into reconfiguring my data usage so that I can use NSArchiver, although I didn't initially want to do that.

Replies

Do you have sandbow enabled (looks like).


Then you must first authorize the access to the folder before creating a file inside.


You will find several threads in this forum about it (serch for sandbox)

Thanks -- but if I search for Sandbox I don't really see anything regarding this. Shouldn't I be able to write to my iOS app's own Documents directory without getting some kind of special access?

Okay I got this working. It's either a roundabout, screwed up way or Apple really doesn't want us writing out files incrementally. Seems braindead, because there are MANY situations where you don't want to create an entire copy of your data-to-write in memory so that you can save it out in one call. I can't believe simply writing a few bytes to a file and reading them back is so complicated in Swift but here is the only way I've found if you're not going the NSArchiver route:


var value = someInt
var data = Data(buffer: UnsafeBufferPointer(start: &value, count: 1))
try data.write(to:fileURL)
       
       
let file: FileHandle? = FileHandle(forUpdatingAtPath: fileURL.path)
file?.seekToEndOfFile()
value = anotherInt
data = Data(buffer: UnsafeBufferPointer(start: &value, count: 1))
file?.write(data)
       
value = yetAnotherInt
data = Data(buffer: UnsafeBufferPointer(start: &value, count: 1))
file?.write(data)

value = aFourthInt
data = Data(buffer: UnsafeBufferPointer(start: &value, count: 1))
file?.write(data)


I couldn't get a FileHandle to write to until I actually used Data.write to create the file -- make sense? NO! After that, you cannot continue to use data.write, like any normal person would think, because apparently data.write does not append, it only overwrites at the beginning of the file. Surely there is some way to do this that I am missing?


THEN, to write more data, you can get FileHandle forUpdatingAtPath because you've now created the file by writing out one piece of data. Thereafter, you can use file.write(data) to write additional items to the file. BUT WAIT - make sure you call file.seekToEndOfFile() before every write, or it won't move past the last thing you wrote.


Sheez -- so complicated to do something so simple -- someone please illuminate me if there's an easier way. And I'm just talking about old-school writing bytes to files sequentially. I am not talking about writing all my data to a file at once, which is easy if you have all your data in one place. And not NSArchiver which is not practival sometimes.


However -- because this is so screwy I am going to go back and look into reconfiguring my data usage so that I can use NSArchiver, although I didn't initially want to do that.

I stumbled across this, and the problem is that you need to create your file first.


In swift:

FileManager.default.createFile(atPath: filepath, contents: nil, attributes: nil)
  • You are right. I've expected FileHandle(forWritingAtPath: outPath) would create and open a file for writing but you have to create the file first. Thanks for this useful answer.

Add a Comment