I want to either organize in place (move and rename) files and folders under a particular directory tree. Probably a sub-folder or the users home. ~/somedir/ with access to all of the subdir items without needing to add those individually. Or scan ~/somedir/ and import to another folder tree ~/outputdir. It could be an external volume ssd.
Sandbox by default wouldn't allow that using nsfilemanager iterators after the SwiftUI folder selection dialogue.
I haven't really looked at the Swift UI APIs for this, but what you're describing works fine with NSOpenPanel. I was actually worried something had changed or I'd gone crazy, so I ended up modifying test project I had laying around JUST to be sure everything worked as I was expecting. See code below.
DISCLAIMER: The code below is greatly simplified test code and shouldn't really be used in any real project. Also, this code is ObjectiveC because it's Friday of WWDC week and that happened to be the starting project I had at hand.
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
NSOpenPanel *myOpenPanel = [NSOpenPanel openPanel];
[myOpenPanel setAllowsMultipleSelection: NO];
myOpenPanel.canChooseDirectories = YES;
[myOpenPanel runModal];
NSURL* fileURL= myOpenPanel.URL;
NSFileManager* fileMan = [NSFileManager defaultManager];
NSURL* newFileURL = [fileURL URLByAppendingPathComponent: @"NewFile.txt"];
NSURL* dirURL = [fileURL URLByAppendingPathComponent: @"MyFolder"];
NSString* junkString = @"Nothing to see here";
BOOL result = true;
result = [fileMan removeItemAtURL: dirURL error: NULL];
NSLog(@"RemoveDirResult %d", result);
#if 1 //Write TestFile
result = [fileMan removeItemAtURL: newFileURL error: NULL];
NSLog(@"RemoveResult %d", result);
result = [junkString writeToURL: newFileURL atomically: YES encoding: NSUTF8StringEncoding error: NULL];
NSLog(@"WriteResult %d", result);
#endif
NSString* readString = [NSString stringWithContentsOfURL: newFileURL encoding: NSUTF8StringEncoding error: NULL];
NSLog(@"readString= %@", readString);
if(result)
{
for (int i = 0; i < 10; i++) {
result = [fileMan createDirectoryAtURL: dirURL withIntermediateDirectories:NO attributes: NULL error:NULL];
NSLog(@"createDirectoryAtURL %d", result);
newFileURL = [dirURL URLByAppendingPathComponent: @"Buriedfile"];
result = [junkString writeToURL: newFileURL atomically: YES encoding: NSUTF8StringEncoding error: NULL];
if(!result)
{
NSLog(@"createDirectoryAtURL %d %@", result, dirURL.path);
break;
}
dirURL = [dirURL URLByAppendingPathComponent: @"MyFolder"];
}
}
}
If you share your SwfitUI code I can take a look, however, from broad experience most issues in this area are caused by one of two things:
-
Switching over to string paths instead of using the URL object. Your app's access to the file is tied to the URL object you receive from the system, so you can't let the object be destroyed until you've either finished your work or used a security scoped bookmark to preserve access. More broadly, using string paths to represent files is a Bad Idea™.
-
Inadvertently manipulating the NSURL in ways that don't actually make sense. Because NSURL is used for both network and file paths, it's easy to write code that appears reasonable but is actually quite odd. Notably, using any of the "...WithString:" URL initializers to create a path is simply wrong, as it initializes the file URL by parsing the path using network RFC. Depending on the input string it will either work fine or completely distort the path.
Kevin Elliott
DTS Engineer, CoreOS/Hardware