URLResourceValues not working for renaming a file - ios

I have to rename some files in a directory in the Documents directory. I'm trying with URLResourceValues:
let fileURLs = try! FileManager.default.contentsOfDirectory(at: directory, includingPropertiesForKeys: nil)
fileURLS.forEach { fileURL in
// calculate newFileName
var resourceValues = URLResourceValues()
resourceValues.name = newFilename
var mutableFileURL = fileURL
try! mutableFileURL.setResourceValues(resourceValues)
}
After setting resourceValues.name I see this in the console...
key NSMutableString "NSURLNameKey" 0x00000001e7a8a368
value NSString "newName.jpg" 0x0000000283f61fe0
So that part is working. The try! completes without crashing so there were no errors thrown. But mutableFileURL is unchanged. It's got the old name, not the new name.
I see in the docs that setting read-only properties or setting properties not supported will be ignored and will not throw an error. But in my research I see this approach used commonly for renaming files. And I don't think it's a write access thing because if I use the old way it works fine:
try! FileManager.default.moveItem(at: fileURL, to: newFileURL)
What could I be missing here?
P.S. app targets iOS 14, running on a real device running iOS 16.1

You have not shown how you know things didn't work. But you say:
But mutableFileURL is unchanged
Your code isn't supposed to change mutableFileURL. If you're looking at mutableFileURL to see what happened, that's the problem.
The URL here is just a pointer to the file on disk. That's the whole point of the setResourceValues approach. Your code changes the name of the file on disk (and mutableFileURL, the pointer, is now invalid and should just be thrown away).
If you re-fetch the contents of the documents directory, you'll see that your code worked just fine.
Complete example, showing both what I think you are doing and how to do this correctly:
// --- create the file ---
let fm = FileManager.default
let docsurl = try fm.url(
for: .documentDirectory, in: .userDomainMask,
appropriateFor: nil, create: false
)
let newurl = docsurl.appending(path: "howdy.txt")
try "howdy".write(to: newurl, atomically: true, encoding: .utf8)
let arr = try fm.contentsOfDirectory(
at: docsurl, includingPropertiesForKeys: nil
)
arr.forEach { print($0.lastPathComponent) }
// howdy.txt
// --- rename the file ---
var values = URLResourceValues()
values.name = "hello.txt"
var mutableurl = newurl
try mutableurl.setResourceValues(values)
// --- how did we do? this is _not_ the way:
print(mutableurl)
// file:///.../Documents/howdy.txt
// --- how did we do? this _is_ the way
let arr2 = try fm.contentsOfDirectory(
at: docsurl, includingPropertiesForKeys: nil
)
arr2.forEach { print($0.lastPathComponent) }
// hello.txt

Related

My app is taking phantom space on my iPad's storage

So, I'm working on an app for iPad that involves drawing.
It stores files in two places: the userDocument :
FileManager.default.url( for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true )
and the appSupportFolder:
FileManager.default.url( for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true )
What happens now is, iOS tells me my app is taking up 18GB of space on disk, even though i cleared all the document directory (using the finder, as this one is visible) and the support folder :
if let suppPath = try? FileManager.default.url( for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true ),
let URLs = try? FileManager.default.contentsOfDirectory( atPath: suppPath.path )
{
for url in URLs
{
let completePath = suppPath.appendingPathComponent( url )
try? FileManager.default.removeItem( at: completePath )
}
}
After this, i don't even know how to check the content of this folder, but i ran this :
func ScanDir( url: URL, depth: Int )
{
var string = ""
for _ in 0 ..< depth
{
string += "----"
}
string += "\(url.lastPathComponent)"
debugPrint( string )
if let URLs = try? FileManager.default.contentsOfDirectory( atPath: url.path )
{
for urlstring in URLs
{
ScanDir( url: url.appendingPathComponent( urlstring ), depth: depth+1 )
}
}
}
The result from the scan is then
"Application Support"
When before, i could see all my projects, exported files, brushes etc...
So it seemed to have worked.
But still, the app is up to 18GB.
Then i can uninstall it, but then as soon as I rebuild the app, it goes back.
I'm so confused right now ...
Alright, as it turns out, iOS automatically puts anything deleted from .documentDirectory in a hidden trash within that same folder.
So all images that i stored on .documentDirectory and then deleted, went in a .trash folder, that i have no idea how to see on the iPad, it just wouldn't let me know that there was this going on. I guess i'm not very familiar with iStuff yet.
So i had to run my cleaning sequence over this folder to get rid of it ..

File name that saved in document directory have space character

I want to download video file and save it in the document directory but when i try show that downloaded video, i can't create URL of that file.
i'm using this code for create path for save file. my file name have spacing : (mobileslider video.mp4), and i want to save this video with own name but i can't create url path that contain space. how can solve this problem?
if let url = URL(string:videoURL){
showVideoFromNib(nibURL:url)
}
when i try to remove space from videoURL, showVideoFromNib method called but don't show the video. and when use orginal url, showVideoFromNib method didn't call.
Edit 1:
i change destination path from :
let destination = DownloadRequest.suggestedDownloadDestination(for: .documentDirectory)
to :
let destination: DownloadRequest.DownloadFileDestination = { _, _ in
let pathComponent = "pack\(self.packID)-\(selectRow + 1).mp4"
let directoryURL: URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let folderPath: URL = directoryURL.appendingPathComponent("Downloads", isDirectory: true)
let fileURL: URL = folderPath.appendingPathComponent(pathComponent)
return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}
now i can create URL and my show video method call but video don't show.
this is my new sample path:
/Users/MyUser/Library/Developer/CoreSimulator/Devices/2C2B6A39-5426-4E2C-B25A-CF64817AF26F/data/Containers/Data/Application/5A1BB109-D452-4F03-8820-EE1120E07C3D/Documents/Downloads/pack7-2.mp4
this file exist in this path but video player don't show this.
I solve problem. i'm using
let path = destinationUrl.absoluteString
instead of
let path = destinationUrl.path

Print all filenames in a folder in iOS application

I have a folder with 4 subfolders in my iOS application with each of these containing about 20 files each. I would like to be able to iterate through each folder and print out the filenames. I am not sure how to go about doing this.
Here is what I have tried:
let docsPath = NSBundle.mainBundle().resourcePath! + "/Samples";
let fileManager = NSFileManager.defaultManager()
var error: NSError?
let docsArray = fileManager.contentsOfDirectoryAtPath(docsPath, error:&error)
println(docsArray)
This prints out nil. I expect it to print out each of the filenames. How do I make this happen?
You have two problems here:
1)
Check your built app to see if "Samples" is really ending up in the built binary. From the error of "The operation couldn’t be completed", I'm thinking you aren't copying "Samples" into the compiled app bundle or at least into the place you're expecting it to be.
2)
The call you're doing will give you the contents of the folder, but not the contents of the subfolders which is what you really want to list.
Use NSDirectoryEnumerator instead to get the contents of that folder and subfolders. Here is a related question that might give you one direction to go.
You can use the NSFileManager's enumerator if you want to get all the files including inside subdirectories.
Simple example:
if let enumerator = fileManager.enumeratorAtURL(docsPath, includingPropertiesForKeys: nil, options: nil, errorHandler: nil) {
while let url = enumerator.nextObject() as? NSURL {
println(url)
}
}
Nevermind, I figured it out:
var docsPath = NSBundle.mainBundle().resourcePath! + "/Snare";
let fileManager = NSFileManager.defaultManager()
var error: NSError?
let docsArray = fileManager.contentsOfDirectoryAtPath(docsPath, error:&error)
//println(error!.localizedDescription)
println(docsArray)
for filename in docsArray!
{
let subfolderPath = docsPath + "/"+(filename as! String)
println(docsPath)
let subarray = fileManager.contentsOfDirectoryAtPath(subfolderPath, error: &error)
println(subarray)
}

Seems like my Text file isn't storing persistently in Documents Directory

NOTE: Possibly Resolved: Was missing ".path!" to documentsUrl.path!.stringByAppendingPathComponent(path)
I am saving a text file to Applications Documents Directory. Using
var path = ""
let date = NSDate()
let file = "\(date).txt"
let joined = "\n".join(ActionArray)
if let dirs : [String] = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.AllDomainsMask, true) as? [String] {
let dir = dirs[0]
path = dir.stringByAppendingPathComponent(file);
println(path)
joined.writeToFile(path, atomically: false, encoding: NSUTF8StringEncoding, error: nil);
}
println path
/Users/dustin/Library/Developer/CoreSimulator/Devices/5EA056FC-114D-4C98-9B35-29B46760BE3A/data/Containers/Data/Application/2AB9FD4A-25AB-4385-9CFB-A66AEFA8C1A3/Documents/2015-05-16 08:24:08 +0000.txt
Then store path and some other data to an Entity, which will be used to call back the text file later.
When I do a fetchRequest on the entity and print out the array I get
(
" (entity: Entity; id: 0xd000000000040000 ; data: {\n date = \"2015-05-16 08:18:20 +0000\";\n path = \"/Users/dustin/Library/Developer/CoreSimulator/Devices/5EA056FC-114D-4C98-9B35-29B46760BE3A/data/Containers/Data/Application/4A9C17AB-57C1-47CB-B9B1-E0E5CA494B4E/Documents/2015-05-16 08:24:08\";\n})"
)
the path is now missing the " +0000". When I call on the path from the Entity.path, my text file is found...BUT when I completely close app (home-swipe-^), the text file search...
let fileContent = String(contentsOfFile: path, encoding: NSUTF8StringEncoding, error: nil)!
var fileContentArr = split(fileContent) {$0 == "\n"}
I get
fatal error: unexpectedly found nil while unwrapping an Optional value
What am I doing wrong to write a persistent text file to draw upon later regardless if App reset?
EDIT: I've taken advice to notice directory path changes between App sessions, So I've instead stored file name to Entity and get Documents Path at read time. I still get error.
I'm doing some debugging and my println's are as follows
let documentsUrl = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0] as! NSURL
// now lets get the directory contents (including folders)
if let directoryContents = NSFileManager.defaultManager().contentsOfDirectoryAtPath(documentsUrl.path!, error: nil) {
println(directoryContents)
println(documentsUrl.path!)
}
[2015-05-17 04:25:48 +0000.txt, 2015-05-17 04:31:07 +0000.txt, iPro_Poker_HH_swift.sqlite, iPro_Poker_HH_swift.sqlite-shm, iPro_Poker_HH_swift.sqlite-wal]
/Users/dustindobrilovic/Library/Developer/CoreSimulator/Devices/5EA056FC-114D-4C98-9B35-29B46760BE3A/data/Containers/Data/Application/7CD25462-947C-40AA-97DB-4A845FED1451/Documents
My Entity Fetch array println()
(
" (entity: Hand; id: 0xd000000000080000 ; data: {\n path = \"2015-05-17 04:31:07 +0000.txt\";\n})"
)
I then combine documentsUrl.path! with path from entity to get println(stringDocumentsURL)
file:/Users/dustindobrilovic/Library/Developer/CoreSimulator/Devices/5EA056FC-114D-4C98-9B35-29B46760BE3A/data/Containers/Data/Application/7CD25462-947C-40AA-97DB-4A845FED1451/Documents/2015-05-17 04:31:07 +0000.txt
This is where Im getting a warning which is very next line.
let fileContent = String(contentsOfFile: stringDocumentsURL, encoding: NSUTF8StringEncoding, error: nil)!
fatal error: unexpectedly found nil while unwrapping an Optional value
>
Path changes across launches. Try to persist only the relative part of the path and reconstruct the absolute path before accessing it.

Couldn't create log directory

When I test my application on iPad it run perfectly i.e. call the database , create folder and perform other task but in console it show me
2015-05-12 11:25:32.478 MyApp[291:19680] [PLLogging] Couldn't create log directory: The operation couldn’t be completed. (Cocoa error 513.).
When I search, found this answer NSFileManager creating folder (Cocoa error 513.)
But not abel to remove this.
Also when I shutdown and restart the iPad this line is not show
Question:
Why i get this statement in console?
The above statement can crash my app in future?
How to remove Cocoa error 513 ?
Here is my code to calling the database
let fileManager = NSFileManager()
var Sourcepath = NSBundle.mainBundle().resourcePath?.stringByAppendingPathComponent("DataBase.db");
let docsPath = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0] as! String
let databaseStr = "DataBase.db"
let dbPath = docsPath.stringByAppendingPathComponent(databaseStr)
println(dbPath)
if(fileManager .fileExistsAtPath(dbPath) == false) {
var error:NSError?
fileManager.copyItemAtPath(Sourcepath!, toPath: dbPath, error: &error)
println(error)
}
and here is a functions which create a logs folder
func CreateLog(Log:String)
{
autoreleasepool{
var formatter:NSDateFormatter! = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd";
var DateString = formatter.stringFromDate(NSDate()).stringByAppendingString("_tempLogs")
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss";
var FileManager:NSFileManager! = NSFileManager.defaultManager()
var LogFolder = NSBundle.mainBundle().resourcePath?.stringByAppendingPathComponent("Logs")
let searchPath = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0] as! String
let LogStr = "Logs"
let LogFolderPath = searchPath.stringByAppendingPathComponent(LogStr)
if(FileManager.fileExistsAtPath(LogFolderPath) == false)
{
var error:NSError?
FileManager.createDirectoryAtPath(LogFolderPath, withIntermediateDirectories: true, attributes: nil, error: &error)
}
var LogPath = LogFolderPath.stringByAppendingPathComponent(DateString).stringByAppendingPathExtension("txt")
var WriteString = (formatter.stringFromDate(NSDate()).stringByAppendingString(" ").stringByAppendingString(Log).stringByAppendingString("\n"));
var Data = WriteString.dataUsingEncoding(NSUTF8StringEncoding)
if(!FileManager.fileExistsAtPath(LogPath!))
{
FileManager.createFileAtPath(LogPath!, contents: Data, attributes: nil)
}
else
{
var output = NSFileHandle(forWritingAtPath: LogPath!);
output?.seekToEndOfFile();
output?.writeData(Data!);
output?.closeFile()
output = nil
}
formatter = nil
FileManager = nil
}
}
The 513 is a permission issue (NSFileWriteNoPermissionError).
Try to println the LogFolderPath and double check if it is correct (or post it here the result).
D.
The problem is that you are trying to create a file in your app's bundle. The bundle is read-only in iOS (and you should treat it as read-only on Mac OS as well, even though it is possible to write to your app bundle in Mac OS.)
Your code will work on the simulator because changing the app bundle is allowed on Mac OS, and the simulator runs under Mac OS.
Change your code to use your documents directory, temp directory, or some other directory in your bundle and you should be fine.
I've been thinking about this scenario, and I can't come up with any better suggestion than: Race Condition?
Could it be that two treads are trying to log at the same time? The first one creates the folder directly after the second tread is passing the .fileExistsAtPath check?
T1: Folder? -> No
T2: Folder? -> No
T1: Create
T2: WTF? There is a folder here already?
If this is the case, it could solve the problem by moving the create directory check and logic to a earlier stage, like on App start or similar.
Not sure this is the issue or that this is the definite answer, so just let's call it a qualified guess... :)
The format you using for getting date string is "yyyy-MM-dd HH:mm:ss" which introduces a space in the result string. Remove the space in the formatter so that it looks like this "yyyy-MM-ddHH:mm:ss" and try the above code or you could replace the space in the resulting path and write data to the file

Resources