Problems deleting file in fileManager(FileManager.default.removeItem) while unit testing - ios

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”.

Related

mock item in cache directory for unit tests

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!)
}

How to initialise an OutputStream with a Url?

I try to create an OutputStream to an app group folder, which is created as follows:
func createProjectDirectoryPath(path:String) -> String
{
let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.xyz")
let logsPath = containerURL!.appendingPathComponent(path)
NSLog("12345- folder path: %#", logsPath.path)
do {
try FileManager.default.createDirectory(atPath: logsPath.path, withIntermediateDirectories: true, attributes: nil)
} catch let error as NSError {
NSLog("12345- Unable to create directory %#", error.debugDescription)
}
return logsPath.path
}
This function gives me a path like this
/private/var/mobile/Containers/Shared/AppGroup/40215F20-4713-4E23-87EF-1E21CCFB45DF/pcapFiles
This folder exists, because the line FileManager.default.fileExists(path) returns true. The next step is to append a generated filename to the path, which I am doing here
let urlToFile = URL(string: createProjectDirectoryPath(path: "pcapFiles").appending("/\(filename)"))
which gives me the correct new path
/private/var/mobile/Containers/Shared/AppGroup/40215F20-4713-4E23-87EF-1E21CCFB45DF/pcapFiles/39CC2DB4-A6D9-412E-BAAF-2FAA4AD70B22.pcap
If I call this line, ostream is always nil
let ostream = OutputStream(url: urlToFile!, append: false)
Do I miss something? The OutputStream should create the file on this path, but for unknown reason, it is not possible.
PS: AppGroup is enabled in Capabilities and in developers console.
Your createProjectDirectoryPath() function returns a file path,
therefore you must use URL(fileURLWithPath:) to convert that to an
URL. Alternatively, modify your function to return an URL instead:
func createProjectDirectoryPath(path:String) -> URL? {
let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.xyz")
let logsURL = containerURL!.appendingPathComponent(path)
do {
try FileManager.default.createDirectory(at: logsURL, withIntermediateDirectories: true)
} catch let error as NSError {
NSLog("Unable to create directory %#", error.debugDescription)
return nil
}
return logsURL
}
In addition, you have to call open() on all Stream objects before
they can be used, this will also create the file if it did not exist
before:
guard let logsURL = createProjectDirectoryPath(path: "pcapFiles") else {
fatalError("Cannot create directory")
}
let urlToFile = logsURL.appendingPathComponent(filename)
guard let ostream = OutputStream(url: urlToFile, append: false) else {
fatalError("Cannot open file")
}
ostream.open()

iOS - Best practices for FileManager extensions

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
}
}
}

Swift 3 Creating a folder throws error "file does not exist" [duplicate]

I have this function to save an image inside the tmp folder
private func saveImageToTempFolder(image: UIImage, withName name: String) {
if let data = UIImageJPEGRepresentation(image, 1) {
let tempDirectoryURL = NSURL.fileURLWithPath(NSTemporaryDirectory(), isDirectory: true)
let targetURL = tempDirectoryURL.URLByAppendingPathComponent("\(name).jpg").absoluteString
print("target: \(targetURL)")
data.writeToFile(targetURL, atomically: true)
}
}
But when I open the temp folder of my app, it is empty. What am I doing wrong to save the image inside the temp folder?
absoluteString is not the correct method to get a file path of
an NSURL, use path instead:
let targetPath = tempDirectoryURL.URLByAppendingPathComponent("\(name).jpg").path!
data.writeToFile(targetPath, atomically: true)
Or better, work with URLs only:
let targetURL = tempDirectoryURL.URLByAppendingPathComponent("\(name).jpg")
data.writeToURL(targetURL, atomically: true)
Even better, use writeToURL(url: options) throws
and check for success or failure:
do {
try data.writeToURL(targetURL, options: [])
} catch let error as NSError {
print("Could not write file", error.localizedDescription)
}
Swift 3/4 update:
let targetURL = tempDirectoryURL.appendingPathComponent("\(name).jpg")
do {
try data.write(to: targetURL)
} catch {
print("Could not write file", error.localizedDescription)
}
A solution is to try to write directly to the complete FileManager() file path like so :
let filePath = FileManager.default.temporaryDirectory.appendingPathComponent("\(id).mp4")
//You can choose a specific directory with FileManager.default.urls(for:in:)
before doing
DispatchQueue.main.async {
do {
try data?.write(to: filePath)
//Write is a success
} catch {
print(error.localizedDescription)
//Error while trying to write
}
}

Permissions denied to create folder in caches or library directory [duplicate]

I have this function to save an image inside the tmp folder
private func saveImageToTempFolder(image: UIImage, withName name: String) {
if let data = UIImageJPEGRepresentation(image, 1) {
let tempDirectoryURL = NSURL.fileURLWithPath(NSTemporaryDirectory(), isDirectory: true)
let targetURL = tempDirectoryURL.URLByAppendingPathComponent("\(name).jpg").absoluteString
print("target: \(targetURL)")
data.writeToFile(targetURL, atomically: true)
}
}
But when I open the temp folder of my app, it is empty. What am I doing wrong to save the image inside the temp folder?
absoluteString is not the correct method to get a file path of
an NSURL, use path instead:
let targetPath = tempDirectoryURL.URLByAppendingPathComponent("\(name).jpg").path!
data.writeToFile(targetPath, atomically: true)
Or better, work with URLs only:
let targetURL = tempDirectoryURL.URLByAppendingPathComponent("\(name).jpg")
data.writeToURL(targetURL, atomically: true)
Even better, use writeToURL(url: options) throws
and check for success or failure:
do {
try data.writeToURL(targetURL, options: [])
} catch let error as NSError {
print("Could not write file", error.localizedDescription)
}
Swift 3/4 update:
let targetURL = tempDirectoryURL.appendingPathComponent("\(name).jpg")
do {
try data.write(to: targetURL)
} catch {
print("Could not write file", error.localizedDescription)
}
A solution is to try to write directly to the complete FileManager() file path like so :
let filePath = FileManager.default.temporaryDirectory.appendingPathComponent("\(id).mp4")
//You can choose a specific directory with FileManager.default.urls(for:in:)
before doing
DispatchQueue.main.async {
do {
try data?.write(to: filePath)
//Write is a success
} catch {
print(error.localizedDescription)
//Error while trying to write
}
}

Resources