URL Containing forward slash doesn't work with NSWorkSpace - objective-c

There exists a file name with forward slash(/) in the system.
For example: URL -> ~/Documents/FolderName/TestFilename/myFile.dmg.
Last Path Component is -> "TestFilename/myFile.dmg"
File Name is -> "TestFilename/myFile.dmg"
Now when i use the below code in my application to reveal in finder kind of stuff with the following one. It fails to does revealing in finder.
NSURL *fileURL = [NSURL URLWithString:#"/Documents/FolderName/TestFilename/myFile.dmg"];
[[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs:#[fileURL]];
Now how do get this resolved for such case and make it revealing in finder for such files. I do have tried with "CFURLCreateStringByAddingPercentEscapes", it doesn't seems to be work. .

This can be achieved if you can segregate file name and file location in your code.
Below is the sample code:
NSString *location = #"Users/Desktop";
NSString *fileName = #"TestFilename/myFile.dmg";
if ([fileName rangeOfString:#"/"].location != NSNotFound)
{
fileName = [fileName stringByReplacingOccurrencesOfString:#"/" withString:#":"];
}
[[NSWorkspace sharedWorkspace] selectFile:[location stringByAppendingPathComponent:fileName] inFileViewerRootedAtPath:location];

Related

How to get the path of user's special folder in macOS?

Is there any API to get the user's special folder, such as the Downloads or Documents folder in macOS?
As per my exprience I am generally use to get Home directory path as per below example.
1st Way :
NSArray * paths = NSSearchPathForDirectoriesInDomains (NSDesktopDirectory, NSUserDomainMask, YES);
NSString * desktopPath = [paths objectAtIndex:0];
For Desktop : NSDesktopDirectory
For Document : NSDocumentDirectory
For Downloads : NSDownloadsDirectory
2nd Way:
Not the best way but we can get path by this method also.
Use Localization for different languages.
$Home Directory of Current User:
[NSURL fileURLWithPath:NSHomeDirectory()];
$Home/Desktop
[NSURL fileURLWithPath:[NSHomeDirectory() stringByAppendingPathComponent:#"Desktop"]];
$Home/Documents :
[NSURL fileURLWithPath:[NSHomeDirectory() stringByAppendingPathComponent:#"Documents"]];
$Home/Downloads :
[NSURL fileURLWithPath:[NSHomeDirectory() stringByAppendingPathComponent:#"Downloads"]];
Hope this will help you to find special folder path.
As already mentioned by others, a more modern approach is to retrieve a URL by using NSFileManager:
NSURL *desktopUrl = [NSFileManager.defaultManager URLsForDirectory:NSDesktopDirectory inDomains:NSUserDomainMask].firstObject;
desktop for Swift 5.x:
var DesktopDir : String = {
let paths = NSSearchPathForDirectoriesInDomains(.desktopDirectory, .userDomainMask, true)
return paths[0]
}()

NSURL path not working, but NSString path working

I saved an object to file and I am now trying to run a check on whether or not that file exists. I have confirmed the path of the file and concluded that the IF statement works when I hard code the path as a NSString, see first block. However, when I try saving the path as a NSURL, and convert it to an NSString so that fileManager can run it's method on it, it does not locate the file at the path. Anything that I am missing here?
LOCATES FILE HERE USING HARD CODE NSSTRING:
[NSKeyedArchiver archiveRootObject:employees toFile:#"/Users/xxx/Documents/employees.plist"];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *employeesPath = #"/Users/xxx/Documents/employees.plist";
if ([fileManager fileExistsAtPath:employeesPath]) {
NSLog(#"It exists! yes!");
}
else {
NSLog(#"Doesn't exist, sorry bud");
}
DOES NOT LOCATE FILE USING NSURL:
[NSKeyedArchiver archiveRootObject:employees toFile:#"/Users/xxx/Documents/employees.plist"];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *employeesPath = [NSURL fileURLWithPath:#"/Users/xxx/Documents/employees.plist"];
NSString *employeesString = [employeesPath absoluteString];
if ([fileManager fileExistsAtPath:employeesString]) {
NSLog(#"It exists! yes!");
}
else {
NSLog(#"Doesn't exist, sorry bud");
}
EDIT
-- if I wanted to use the NSURL method, I could by making a function to store the path into a NSString the proper way. This ended up working:
NSString* getPropertyListPath() {
NSURL *documentDir = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
NSURL *plist = [documentDir URLByAppendingPathComponent:#"employees.plist"];
return plist.path;
}
You are correct to use NSURL, giuseppe, instead of string literals.
NSURL is more compatible and robust than a string literal. NSURL types will give you access to more methods and functionality.
The trick that you stumbled into is that you needed the file path without the "scheme" or "domain" included in the format.
You are correct to call the path method on your NSURL object to retrieve the correct path format for what you need. The path method only returns the path component to the NSURL address path. It doesn't return the scheme or domain components.
NSString *correctPathFormat = [yourNsurlObject path];
For Example:
If I have a file in the following directory path:
NSString* myDirPath = #"/Users/yourUserName/imageFolder";
and load this into a NSURL object:
NSURL *nsurlDirPath = [NSURL fileURLWithPath:myDirPath];
then append the file name and file type:
NSURL *nsurlFilePath = [nsurlDirPath URLByAppendingPathComponent:#"employee.plist"];
If you call the [nsurlFilePath absoluteString] method you will get an NSString value in the format of "scheme://domain/path"
NSString *retrievePath = [nsurlFilePath absoluteString];
NSLog(#"%#",retrievePath);
This logs out:
file:///Users/yourUserName/imageFolder/employee.plist
Special Note: This is the equivalent to the file path:
file://localhost/Users/yourUserName/imageFolder/employee.plist
The "localhost" is just omitted because this is by default implied, so that is why you see the tripple "///" in "file:///Users/...".
"localhost" is an alias that refers to the local device's ip address, or in other words, the device the code is running on.
Finally, to get the correct path format you need you would run the 'path' method on the NSURL object, which takes us back to the answer at the very beginning of my response:
NSString *correctPathFormat = [nsurlFilePath path];
This logs out the correct "path" component, minus the "scheme" & "domain":
/Users/yourUserName/imageFolder/employee.plist
Further Explanation:
NSURLS have three parts:
scheme : [http, https, ftp, file]
domain : [www.stackoverflow.com, localhost, ipAddress]
path : [/questions/26663573/, /Users/youUserName/subDirName]
scheme | domain | path
file://localhost/Users/youruserName/file.txt
Don't use a NSURL as a file path intermediary.
NSURL *employeesPath = [NSURL fileURLWithPath:#"/Users/xxx/Documents/employees.plist"];
NSString *employeesString = [employeesPath absoluteString];
NSLog(#"employeesString: %#", 'employeesString');
Output:
employeesString: 'file:///Users/xxx/Documents/employees.plist'
Which is clearly not a file path.

How do I copy a directory that is inside of my main bundle to an NSPasteBoard?

I have been tearing my hair trying different combinations of file paths and different types but basically what I am trying to do is copy a folder called "test" that is inside of my resources folder.
Copying folders onto the NSPasteBoard works when I give an absolute path (ex: /Users/dw/src/Menulet) but it doesn't work when I try using my mainBundle's resource path.
Here is the code I currently have:
NSString *resourceFolder = [[NSURL fileURLWithPath:[[NSBundle mainBundle]
resourcePath]] absoluteString];
NSPasteboard *pboard = [NSPasteboard pasteboardWithName:NSDragPboard];
[pboard declareTypes:[NSArray arrayWithObject:NSURLPboardType] owner:nil];
NSString *SDKPathString = [[NSString alloc] initWithFormat:#"%#test/",
resourceFolder];
NSURL *SDKPathURL = [[NSURL alloc] initWithString:SDKPathString];
[pboard writeObjects:[NSArray arrayWithObject:SDKPathURL]];
The result is that it can't find the file:
__CFPasteboardIssueSandboxExtensionForPath: error for [/Users/dw/Library/Developer/Xcode/DerivedData/Menulet-bvwpfkjlcufhxubvaxubgnubtfgi/Build/Products/Debug/Menulet.app/Contents/Resources/test]
How can I copy the directory?
Edit:
My guess is that you're missing a '/' character when you do this:
NSString *SDKPathString = [[NSString alloc] initWithFormat:#"%#test/",
resourceFolder];
Indeed resourceFolder is a path, I usually use something like this instead:
NSString* SDKPathString= [resourceFolder stringByAppendingPathComponent: #"test"];
Even if in your case you could simply correct the error by putting a '/' character before "test".But I think this is more mnemonic.
Update
Isn't said that if you create a group Xcode also creates a folder. If you want to be sure that Xcode does so, create a folder with finder and drag it to the project. Check these options:
This way the folder is actually created, and also added to the bundle.
Update 2
You shouldn't try to move your directory inside the project folder to put into the right place, instead put it wherever you want in the bundle, provided that the directory is copied in the bundle.
Once did so, the bundle will manage everything for you. So just search the directory using:
- (NSString *)pathForResource:(NSString *)name ofType:(NSString *)extension;
So in your case:
NSBundle* bundle= [NSBundle mainBundle];
NSString *SDKPathString = [ bundle pathForResource: #"test" ofType: #""];

LSCopyApplicationURLsForURL always returns null

When I try to log all available editors on my system for my temporary file (which is "toString" in this code) it always returns null, although I have many applications installed on my system.
NSArray *appUrls = (NSArray*)LSCopyApplicationURLsForURL((CFURLRef)[NSURL URLWithString:toString], kLSRolesViewer | kLSRolesEditor);
toString is containing the following file path:
/var/folders/pl/tcc5k3fd6tj2__9dprg9dm1m0000gp/T/tempFile
What should be the problem here?
[NSURL URLWithString:toString]
expects a complete URL string including scheme, such as "file://var/folders/...".
Use
[NSURL fileURLWithPath:toString]
instead to get a file URL with the specified path.
Another problem could be that your file name does not have any file extension (e.g. ".txt"), because Launch Services uses the extension (or file type/creator) to find a suitable application.
I was struggling with the this and I wanted to get all Bundles that could open a determined path/file extension.
If you have a file extension, you can get all bundles that can edit it by the following:
//All Bundle Ids
NSString *pathExtension = #"docx";
CFArrayRef utisRef = UTTypeCreateAllIdentifiersForTag(kUTTagClassFilenameExtension,(__bridge CFStringRef) pathExtension,nil);
NSLog( #"UTI: utisRef %#", utisRef);
NSArray *utis = CFBridgingRelease(utisRef);
NSMutableSet *mutableSet = [[NSMutableSet alloc] init];
for (NSString *uti in utis) {
CFArrayRef bundleIDsRef = LSCopyAllRoleHandlersForContentType((__bridge CFStringRef) uti,kLSRolesEditor);
[mutableSet addObjectsFromArray:CFBridgingRelease(bundleIDsRef)];
}
NSLog( #"bundleIDs: %#", mutableSet);
If you have a path of file and you want to get all apps location that can edit it, you can use the following:
//Location of apps
NSString *str = #"/Users/ricardoanjos/Library/Developer/Xcode/DerivedData/EgnyteDrive-hforbniifiojczefbnwanzxakvlr/Build/Products/Debug/1.pdf";
NSURL* url = [[NSURL alloc] initFileURLWithPath:str];
CFURLRef urlRef = (__bridge CFURLRef)url;
CFArrayRef appUrlsRef = LSCopyApplicationURLsForURL(urlRef, kLSRolesEditor);
NSArray *appUrls = CFBridgingRelease(appUrlsRef);
NSLog(#"appUrls: %#", appUrls);
I hope it will help.

NSFileManager finds files inside a folder only when it's running under a debugger

When I run the following code under the Xcode debugger it successfully finds the package with .app extension, but when I run it standalone "file" object is nil. In fact when I did NSLogs folderEnum was also nil. Note that folderPath points to a folder that is in the same directory as the the program executable.
NSFileManager *localFileManager = [[NSFileManager alloc] init];
NSDirectoryEnumerator *folderEnum = [localFileManager enumeratorAtPath:folderPath];
NSString *file;
while (file = [folderEnum nextObject]) {
if ([[file pathExtension] isEqualToString: #"app"]) {
break;
}
}
Any ideas? Something to do with the Mac system file permissions?
Edit
I should have probably mentioned that folderPath was actually a relative path and not an absolute one. So I changed folderPath to be relative to [[NSBundle mainBundle] bundlePath] path and it works now. But if anyone can shed some light why relative path doesn't work that be great.
Does changing the first line to:
NSFileManager *localFileManager = [NSFileManager defaultManager];
make any difference? Are you just trying to get the path for your application? (There are easier ways)