I'm currently writing unit tests that involve files in the cache directory. The problem that is happening is when the app is launched for the first time the cache related tests fail with errors like:
The file “5b063e275d506f65ebf1b02d926f19a4” couldn’t be opened because
there is no such file.
I believe the reason for this is because since the app is being opened for first time there is nothing in the cache directory. Is there a way to insert files in the cache directory in the unit test itself so this error isn't thrown?
This is a sample unit test:
internal func filePathForFileName(name: String) -> String {
let cyptoName = name.md5()
return getCacheFolderPath() + "/" + (cyptoName ?? "")
}
internal func getCacheFolderPath() -> String {
var folderPath = ""
guard let path = self.urls(for:.cachesDirectory , in: .userDomainMask).last?.path else {
return folderPath }
folderPath = path + "/NetworkCache"
if !self.fileExists(atPath: folderPath) {
do {
try self.createDirectory(atPath: folderPath, withIntermediateDirectories: false, attributes: nil)
}
catch {
print(error.localizedDescription)
}
}
return folderPath
}
}
func saveData(data: Data, forFileName fileName: String, completionHandler: #escaping (URL?)->()) {
let url = URL(fileURLWithPath: self.filePathForFileName(name: fileName))
print("saveData:\(url)")
do {
try data.write(to: url, options: .atomic)
completionHandler(url)
} catch {
print(error.localizedDescription)
completionHandler(nil)
}
}
let filePath = "fileName"
func test_saveData(){
let promise = expectation(description: "Completion Hander invoked")
var isSaved: Bool?
let path = self.sut!.filePathForFileName(name: self.filePath)
sut?.saveData(data: mockData!, forFileName: filePath, completionHandler: {_ in
let fileManager = MockFileManager.default
if fileManager.fileExists(atPath: path) {
isSaved = true
print("FILE AVAILABLE")
} else {
isSaved = false
print("FILE NOT AVAILABLE")
}
promise.fulfill()
})
wait(for: [promise], timeout: 10, enforceOrder: true)
XCTAssertTrue(isSaved!)
}
Related
I need to save a video file to a temp directory and save the reference to it URL. Currently I have tried using fileManager to create a temp directory and then createFile(atPath: tempDirString, contents: vidData, attributes: nil). But I dont/am not able to save the full reference to the file.
What I have tried:
PHCachingImageManager().requestAVAsset(forVideo: (cell?.assetPH)!, options: nil) { (avAsset, _, _) in
if let avAsset = avAsset {
print(avAsset, " the avasset?")
// vidAVAsset = avAsset
let avPlayerItem = AVPlayerItem(asset: avAsset)
let avPlayer = AVPlayer(playerItem: avPlayerItem)
print(avPlayer, "<-- good?")
let finalURL = self.urlOfCurrentlyPlayingInPlayer(player: avPlayer)
print(finalURL, "<-- finalURL YEAH EYAHY YEAH?!?!?")
// newUserTakenVideo.videoURL = finalURL
let url = self.addVidToTempURL(vidURL: finalURL!)
newUserTakenVideo.videoURL = url
// let newUserTakenVideo = SelectedMedia(videoURL: finalURL, phAsset: (cell?.assetPH)!, thumbnailImg: (cell?.imageView.image)!, uniqueCellID: indexPath)
// GlobalSharedData.shared.arrayOfCurrentCreateMedia.append(newUserTakenVideo)
} else {
print("did noot work")
}
}
This is the function called:
func addVidToTempURL(vidURL: URL) -> URL? {
let fileManager = FileManager.default
let tempDir = fileManager.temporaryDirectory
let tempDirString = tempDir.path
do {
print( "tempDir: \(tempDir)" )
print( "tempDirString: \(tempDirString)" )
if fileManager.fileExists(atPath: tempDirString ) {
print( "tempDir exists" )
do {
try fileManager.createDirectory( at: tempDir, withIntermediateDirectories: true, attributes: nil )
print( "tempDir created" )
if fileManager.fileExists(atPath: tempDirString ) {
print( "tempDir exists" )
let vidData = try Data(contentsOf: vidURL)
fileManager.createFile(atPath: tempDirString, contents: vidData, attributes: nil)
print(tempDirString, " the stringfsdsda")
// fileManager.urls(for: fileManager.temporaryDirectory, in: fileManager.)
let url = URL(string: tempDirString)
return url
} else {
print( "tempDir STILL DOES NOT exist" )
return nil
}
} catch {
print( "tempDir NOT created" )
return nil
}
} else {
print( "tempDir DOES NOT exist" )
do {
try fileManager.createDirectory( at: tempDir, withIntermediateDirectories: true, attributes: nil )
print( "tempDir created" )
if fileManager.fileExists(atPath: tempDirString ) {
print( "tempDir exists" )
let vidData = try Data(contentsOf: vidURL)
fileManager.createFile(atPath: tempDirString, contents: vidData, attributes: nil)
print(tempDirString, " the fsdfdsdfsdfsfsd")
let url = URL(string: tempDirString)
return url
} else {
print( "tempDir STILL DOES NOT exist" )
return nil
}
} catch {
print( "tempDir NOT created" )
return nil
}
}
}
}
How can I get a URL reference to this files location?
I appreciate any help and can add more information if needed. Thanks, me
Since you don't seem to want your file to be visible to users or persisted between app launches, the Temporary directory sounds perfectly fine for your use case:
var tempVideoFileUrl: URL {
return FileManager.default.temporaryDirectory.appendingPathComponent("my_video_name")
}
func storeVideoToTemporaryFolder(videoData: Data) {
guard !FileManager.default.fileExists(atPath: tempVideoFileUrl.path) else {
return
}
do {
try videoData.write(to: tempVideoFileUrl)
}
catch {
fatalError()
}
}
func loadVideoFromTemporaryFolder() -> Data? {
if let data = try? Data(contentsOf: tempVideoFileUrl) {
return data
}
return nil
}
Worth mentioning though, the system may (and most likely will) purge this directory after the app is exited. It's recommended that you remove any temporary directories/files after they're no longer needed.
So in your case, you can simply remove it once you finished uploading to Firebase Storage:
func deleteVideoFromTemporaryFolder() {
do {
try FileManager.default.removeItem(at: videoFileUrl)
}
catch {
fatalError()
}
}
If you prefer to keep your file around between app launches though, you could use Application Support directory. But since Application Support and Documents directories gets automatically backed up, you may want to exclude your file from iCloud backup by setting its URL's isExcludedFromBackupKey key:
var applicationSupportVideoFileUrl: URL {
let applicationSupportFolderUrl = try! FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
return applicationSupportFolderUrl.appendingPathComponent("my_video_name")
}
func excludeFromCloudBackup(url: URL) {
var targetUrl = url
var isAlreadyExcludedFromBackup: Bool
do {
let storedRessourceValues = try targetUrl.resourceValues(forKeys: [URLResourceKey.isExcludedFromBackupKey])
isAlreadyExcludedFromBackup = storedRessourceValues.isExcludedFromBackup ?? false
}
catch {
fatalError()
}
guard !isAlreadyExcludedFromBackup else {
return
}
var ressourceValues = URLResourceValues()
ressourceValues.isExcludedFromBackup = true
do {
try targetUrl.setResourceValues(ressourceValues)
}
catch {
fatalError()
}
}
Edit: To get the data from your PHAsset, this should work:
import Photos
func loadVideoData(phAsset: PHAsset, completion: #escaping (Data?)->()) {
guard phAsset.mediaType == .video else {
return completion(nil)
}
let options = PHVideoRequestOptions()
options.isNetworkAccessAllowed = true
options.deliveryMode = .highQualityFormat
PHCachingImageManager().requestAVAsset(forVideo: phAsset, options: options) { (avAsset, _, _) in
guard let avUrlAsset = avAsset as? AVURLAsset else {
return
}
var videoData: Data?
do {
videoData = try Data(contentsOf: avUrlAsset.url)
} catch {
fatalError()
}
DispatchQueue.main.async {
completion(videoData)
}
}
}
Then simply call this method and store your video in the Temporary folder:
loadVideoData(phAsset: yourPhAsset) { [weak self] videoData in
guard let strongSelf = self else { return }
guard let videoData = videoData else {
return
}
strongSelf.storeVideoToTemporaryFolder(videoData: videoData)
}
Currently I'm trying to create files during a unit test to see if I can retrieve the data after its created but the fileManager.createFileAtPath always fails. I looked at this post: fileManager.createFileAtPath always fails I implemented the solutions to that answer but it still fails. This is what the unit test and accompanying function looks like:
internal func getCacheFolderPath() -> String {
var folderPath = ""
guard let path = self.urls(for:.cachesDirectory , in: .userDomainMask).last?.path else {
return folderPath
}
folderPath = path + "/NetworkCache"
if !self.fileExists(atPath: folderPath) {
do {
try self.createDirectory(atPath: folderPath, withIntermediateDirectories: false, attributes: nil)
} catch {
print(error.localizedDescription)
}
}
return folderPath
}
func test_createFile(){
let path = sut?.getCacheFolderPath()
let fileManager = FileManager.default
if !FileManager.default.fileExists(atPath: path!) {
do {
try FileManager.default.createDirectory(atPath: path!, withIntermediateDirectories: true, attributes: nil)
} catch let error as NSError {
print(error.localizedDescription)
}
}
let isFileCreated = fileManager.createFile(atPath: path!, contents: mockData, attributes: nil)
print(isFileCreated)//This always returns false
}
Using the data.write(to:) instead of create file helped determine what the issue was. It looks like I was creating a folder as a file path and trying to save a file to that path. Since it was a file and not a folder it was returning that something already exists there.
Here is what the completed working code looks like:
func saveData(data: Data, forFileName fileName: String, completionHandler: #escaping (URL?)->()) {
let url = URL(fileURLWithPath: self.filePathForFileName(name: fileName))
print("saveData:\(url)")
do {
// [data writeToFile:[self thdncFilePathForFileName:name] atomically:YES];
try data.write(to: url, options: .atomic)
completionHandler(url)
} catch {
print(error.localizedDescription)
completionHandler(nil)
}
}
func test_createFile(){
var isSaved: Bool = false
let path = sut?.getCacheFolderPath()
let promise = expectation(description: "Completion Hander invoked")
if !FileManager.default.fileExists(atPath: path!)
{
do {
try FileManager.default.createDirectory(atPath: path!, withIntermediateDirectories: true, attributes: nil)
} catch let error as NSError {
print(error.localizedDescription)
}
}
sut?.saveData(data: mockData!, forFileName: filePath, completionHandler: { (url) in
let fileManager = MockFileManager.default
if fileManager.fileExists(atPath: self.sut!.filePathForFileName(name: self.filePath)) {
isSaved = true
print("FILE AVAILABLE")
} else {
isSaved = false
print("FILE NOT AVAILABLE")
}
promise.fulfill()
})
wait(for: [promise], timeout: 10, enforceOrder: true)
XCTAssertTrue(isSaved)
}
Currently there are issues with files not being deleted. I've looked at other posts like: FileManager.default.removeItem not removing file [duplicate]
and Error deleting contents in directory - Domain=NSCocoaErrorDomain Code=4 | Domain=NSPOSIXErrorDomain Code=2 “No such file or directory”
I'm getting the same error “No such file or directory”. This is happening with this function when it's being unit tested:
internal func getCacheFolderPath() -> String {
var folderPath = ""
guard let path = self.urls(for:.cachesDirectory , in: .userDomainMask).last?.path else { return folderPath }
folderPath = path + "/NetworkCache"
if !self.fileExists(atPath: folderPath) {
do {
try self.createDirectory(atPath: folderPath, withIntermediateDirectories: false, attributes: nil)
}
catch {
print(error.localizedDescription)
}
}
return folderPath
}
internal func filePathForFileName(name: String) -> String {
let cyptoName = name.md5()
return getCacheFolderPath() + "/" + (cyptoName ?? "")
}
func deleteFile(forFile fileName: String){
let path = self.filePathForFileName(name: fileName)
if self.fileExists(atPath: path) {
do {
try self.removeItem(atPath: path)
// print(path)
} catch {
print(error.localizedDescription)
}
}
}
This is what the unit test looks like:
func test_deleteFile(){
let path = sut!.filePathForFileName(name: fileName)
sut?.deleteFile(forFile: fileName)
print(path)
if sut!.fileExists(atPath: path){
let filemanager = FileManager.default
do {
try filemanager.removeItem(atPath: path)
} catch {
print ("The file could not be removed: \(error)")
}
}
}
FYI I am using a mockFileSystem that is subclassed from FileManager. Even when I try and delete the path manually in the do, try, catch block it still returns the error “No such file or directory”.
I created this FileManager extension. With this extension, I want to create a file hierarchy like so:
Application Support
Favorites
Feed
Images
This is the code I have in FileManager extension which I would call in app delegate as soon as the app launches. Then I would use this code to always retrieve the path's of the folders.
Is this a good way to create this hierarchy and retrieve the paths when I need them? Is this good practice?
extension FileManager {
static func createOrFindApplicationDirectory() -> URL? {
let bundleID = Bundle.main.bundleIdentifier
// Find the application support directory in the home directory.
let appSupportDir = self.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
guard appSupportDir.count > 0 else {
return nil
}
// Append the bundle ID to the URL for the Application Support directory.
let dirPath = appSupportDir[0].appendingPathComponent(bundleID!)
// If the directory does not exist, this method creates it.
do {
try self.default.createDirectory(at: dirPath, withIntermediateDirectories: true, attributes: nil)
return dirPath
} catch let error {
print("Error creating Application Support directory with error: \(error)")
return nil
}
}
static func createOrFindFavoritesDirectory() -> URL? {
guard let appSupportDir = createOrFindApplicationDirectory() else {
return nil
}
let dirPath = appSupportDir.appendingPathComponent("Favorites")
// If the directory does not exist, this method creates it.
do {
try self.default.createDirectory(at: dirPath, withIntermediateDirectories: true, attributes: nil)
return dirPath
} catch let error {
print("Error creating Favorites directory with error: \(error)")
return nil
}
}
static func createOrFindFeedDirectory() -> URL? {
guard let appSupportDir = createOrFindFavoritesDirectory() else {
return nil
}
let dirPath = appSupportDir.appendingPathComponent("Feed")
// If the directory does not exist, this method creates it.
do {
try self.default.createDirectory(at: dirPath, withIntermediateDirectories: true, attributes: nil)
return dirPath
} catch let error {
print("Error creating Favorites directory with error: \(error)")
return nil
}
}
static func currentImagesDirectory() -> URL? {
guard let feedDir = createOrFindFeedDirectory() else {
return nil
}
let dirPath = feedDir.appendingPathComponent("Images")
// If the directory does not exist, this method creates it.
do {
try self.default.createDirectory(at: dirPath, withIntermediateDirectories: true, attributes: nil)
return dirPath
} catch let error {
print("Error creating Images directory with error: \(error)")
return nil
}
}
}
It looks pretty good, but you could combine a bit of the code and have better error checking:
extension FileManager {
static func createOrFindApplicationDirectory() -> URL? {
guard let bundleID = Bundle.main.bundleIdentifier else {
return nil
}
// Find the application support directory in the home directory.
let appSupportDirArray = self.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
guard let appSupportDir = appSupportDirArray.first else {
return nil
}
// Append the bundle ID to the URL for the Application Support directory.
let dirPath = appSupportDir.appendingPathComponent(bundleID)
// If the directory does not exist, this method creates it.
do {
try self.default.createDirectory(at: dirPath, withIntermediateDirectories: true, attributes: nil)
return dirPath
} catch let error {
print("Error creating Application Support directory with error: \(error)")
return nil
}
}
static func createOrFindDirectory(named name: String) -> URL? {
guard let appSupportDir = createOrFindApplicationDirectory() else {
return nil
}
let dirPath = appSupportDir.appendingPathComponent(name)
// If the directory does not exist, this method creates it.
do {
try self.default.createDirectory(at: dirPath, withIntermediateDirectories: true, attributes: nil)
return dirPath
} catch let error {
print("Error creating \(name) directory with error: \(error)")
return nil
}
}
static func currentImagesDirectory() -> URL? {
guard let feedDir = createOrFindDirectory(named: "Feed") else {
return nil
}
let dirPath = feedDir.appendingPathComponent("Images")
// If the directory does not exist, this method creates it.
do {
try self.default.createDirectory(at: dirPath, withIntermediateDirectories: true, attributes: nil)
return dirPath
} catch let error {
print("Error creating Images directory with error: \(error)")
return nil
}
}
}
I already have read Read and write data from text file
I need to append the data (a string) to the end of my text file.
One obvious way to do it is to read the file from disk and append the string to the end of it and write it back, but it is not efficient, especially if you are dealing with large files and doing in often.
So the question is "How to append string to the end of a text file, without reading the file and writing the whole thing back"?
so far I have:
let dir:NSURL = NSFileManager.defaultManager().URLsForDirectory(NSSearchPathDirectory.CachesDirectory, inDomains: NSSearchPathDomainMask.UserDomainMask).last as NSURL
let fileurl = dir.URLByAppendingPathComponent("log.txt")
var err:NSError?
// until we find a way to append stuff to files
if let current_content_of_file = NSString(contentsOfURL: fileurl, encoding: NSUTF8StringEncoding, error: &err) {
"\(current_content_of_file)\n\(NSDate()) -> \(object)".writeToURL(fileurl, atomically: true, encoding: NSUTF8StringEncoding, error: &err)
}else {
"\(NSDate()) -> \(object)".writeToURL(fileurl, atomically: true, encoding: NSUTF8StringEncoding, error: &err)
}
if err != nil{
println("CANNOT LOG: \(err)")
}
Here's an update for PointZeroTwo's answer in Swift 3.0, with one quick note - in the playground testing using a simple filepath works, but in my actual app I needed to build the URL using .documentDirectory (or which ever directory you chose to use for reading and writing - make sure it's consistent throughout your app):
extension String {
func appendLineToURL(fileURL: URL) throws {
try (self + "\n").appendToURL(fileURL: fileURL)
}
func appendToURL(fileURL: URL) throws {
let data = self.data(using: String.Encoding.utf8)!
try data.append(fileURL: fileURL)
}
}
extension Data {
func append(fileURL: URL) throws {
if let fileHandle = FileHandle(forWritingAtPath: fileURL.path) {
defer {
fileHandle.closeFile()
}
fileHandle.seekToEndOfFile()
fileHandle.write(self)
}
else {
try write(to: fileURL, options: .atomic)
}
}
}
//test
do {
let dir: URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last! as URL
let url = dir.appendingPathComponent("logFile.txt")
try "Test \(Date())".appendLineToURL(fileURL: url as URL)
let result = try String(contentsOf: url as URL, encoding: String.Encoding.utf8)
}
catch {
print("Could not write to file")
}
Thanks PointZeroTwo.
You should use NSFileHandle, it can seek to the end of the file
let dir:NSURL = NSFileManager.defaultManager().URLsForDirectory(NSSearchPathDirectory.CachesDirectory, inDomains: NSSearchPathDomainMask.UserDomainMask).last as NSURL
let fileurl = dir.URLByAppendingPathComponent("log.txt")
let string = "\(NSDate())\n"
let data = string.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
if NSFileManager.defaultManager().fileExistsAtPath(fileurl.path!) {
var err:NSError?
if let fileHandle = NSFileHandle(forWritingToURL: fileurl, error: &err) {
fileHandle.seekToEndOfFile()
fileHandle.writeData(data)
fileHandle.closeFile()
}
else {
println("Can't open fileHandle \(err)")
}
}
else {
var err:NSError?
if !data.writeToURL(fileurl, options: .DataWritingAtomic, error: &err) {
println("Can't write \(err)")
}
}
A variation over some of the posted answers, with following characteristics:
based on Swift 5
accessible as a static function
appends new entries to the end of the file, if it exists
creates the file, if it doesn't exist
no cast to NS objects (more Swiftly)
fails silently if the text cannot be encoded or the path does not exist
class Logger {
static var logFile: URL? {
guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return nil }
let formatter = DateFormatter()
formatter.dateFormat = "dd-MM-yyyy"
let dateString = formatter.string(from: Date())
let fileName = "\(dateString).log"
return documentsDirectory.appendingPathComponent(fileName)
}
static func log(_ message: String) {
guard let logFile = logFile else {
return
}
let formatter = DateFormatter()
formatter.dateFormat = "HH:mm:ss"
let timestamp = formatter.string(from: Date())
guard let data = (timestamp + ": " + message + "\n").data(using: String.Encoding.utf8) else { return }
if FileManager.default.fileExists(atPath: logFile.path) {
if let fileHandle = try? FileHandle(forWritingTo: logFile) {
fileHandle.seekToEndOfFile()
fileHandle.write(data)
fileHandle.closeFile()
}
} else {
try? data.write(to: logFile, options: .atomicWrite)
}
}
}
Here is a way to update a file in a much more efficient way.
let monkeyLine = "\nAdding a 🐵 to the end of the file via FileHandle"
if let fileUpdater = try? FileHandle(forUpdating: newFileUrl) {
// Function which when called will cause all updates to start from end of the file
fileUpdater.seekToEndOfFile()
// Which lets the caller move editing to any position within the file by supplying an offset
fileUpdater.write(monkeyLine.data(using: .utf8)!)
// Once we convert our new content to data and write it, we close the file and that’s it!
fileUpdater.closeFile()
}
Here's a version for Swift 2, using extension methods on String and NSData.
//: Playground - noun: a place where people can play
import UIKit
extension String {
func appendLineToURL(fileURL: NSURL) throws {
try self.stringByAppendingString("\n").appendToURL(fileURL)
}
func appendToURL(fileURL: NSURL) throws {
let data = self.dataUsingEncoding(NSUTF8StringEncoding)!
try data.appendToURL(fileURL)
}
}
extension NSData {
func appendToURL(fileURL: NSURL) throws {
if let fileHandle = try? NSFileHandle(forWritingToURL: fileURL) {
defer {
fileHandle.closeFile()
}
fileHandle.seekToEndOfFile()
fileHandle.writeData(self)
}
else {
try writeToURL(fileURL, options: .DataWritingAtomic)
}
}
}
// Test
do {
let url = NSURL(fileURLWithPath: "test.log")
try "Test \(NSDate())".appendLineToURL(url)
let result = try String(contentsOfURL: url)
}
catch {
print("Could not write to file")
}
In order to stay in the spirit of #PointZero Two.
Here an update of his code for Swift 4.1
extension String {
func appendLine(to url: URL) throws {
try self.appending("\n").append(to: url)
}
func append(to url: URL) throws {
let data = self.data(using: String.Encoding.utf8)
try data?.append(to: url)
}
}
extension Data {
func append(to url: URL) throws {
if let fileHandle = try? FileHandle(forWritingTo: url) {
defer {
fileHandle.closeFile()
}
fileHandle.seekToEndOfFile()
fileHandle.write(self)
} else {
try write(to: url)
}
}
}
Update: I wrote a blog post on this, which you can find here!
Keeping things Swifty, here is an example using a FileWriter protocol with default implementation (Swift 4.1 at the time of this writing):
To use this, have your entity (class, struct, enum) conform to this protocol and call the write function (fyi, it throws!).
Writes to the document directory.
Will append to the text file if the file exists.
Will create a new file if the text file doesn't exist.
Note: this is only for text. You could do something similar to write/append Data.
import Foundation
enum FileWriteError: Error {
case directoryDoesntExist
case convertToDataIssue
}
protocol FileWriter {
var fileName: String { get }
func write(_ text: String) throws
}
extension FileWriter {
var fileName: String { return "File.txt" }
func write(_ text: String) throws {
guard let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
throw FileWriteError.directoryDoesntExist
}
let encoding = String.Encoding.utf8
guard let data = text.data(using: encoding) else {
throw FileWriteError.convertToDataIssue
}
let fileUrl = dir.appendingPathComponent(fileName)
if let fileHandle = FileHandle(forWritingAtPath: fileUrl.path) {
fileHandle.seekToEndOfFile()
fileHandle.write(data)
} else {
try text.write(to: fileUrl, atomically: false, encoding: encoding)
}
}
}
All answers (as of now) recreate the FileHandle for every write operation. This may be fine for most applications, but this is also rather inefficient: A syscall is made, and the filesystem is accessed each time you create the FileHandle.
To avoid creating the filehandle multiple times, use something like:
final class FileHandleBuffer {
let fileHandle: FileHandle
let size: Int
private var buffer: Data
init(fileHandle: FileHandle, size: Int = 1024 * 1024) {
self.fileHandle = fileHandle
self.size = size
self.buffer = Data(capacity: size)
}
deinit { try! flush() }
func flush() throws {
try fileHandle.write(contentsOf: buffer)
buffer = Data(capacity: size)
}
func write(_ data: Data) throws {
buffer.append(data)
if buffer.count > size {
try flush()
}
}
}
// USAGE
// Create the file if it does not yet exist
FileManager.default.createFile(atPath: fileURL.path, contents: nil)
let fileHandle = try FileHandle(forWritingTo: fileURL)
// Seek will make sure to not overwrite the existing content
// Skip the seek to overwrite the file
try fileHandle.seekToEnd()
let buffer = FileHandleBuffer(fileHandle: fileHandle)
for i in 0..<count {
let data = getData() // Your implementation
try buffer.write(data)
print(i)
}