LSCopyApplicationURLsForURL always returns null - objective-c

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.

Related

My OSX app is sandboxed and I am not able to read data from file by specifying the absolute path

I am completely new to objective C and currently I am trying to advance the functionality of an already existing project.
There is a finder extension in the project which on getting clicked performs an action inside (IBAction) Share(id) sender.
Inside this action , I want to read a file from a particular location (the file contains the port number) and using that port I want to connect to the server.
But what I found was when I click on this extension , nothing happens because it tries to go and read data from the file and is not able to read anything.
I tried to debug this by printing out whatever it has read to some other file but all it printed was blank confirming that it is not able to read the data. Below is my code trying to read the port from a temporary location :
- (IBAction)privateShareAction:(id)sender {
NSFileManager *filemgr;
filemgr = [NSFileManager defaultManager];
if ([filemgr fileExistsAtPath: #"/var/folders/y3/jv117_75505fnk8htdrs0qm40000gr/T/com.aprivacy.xmlCorePort.properties" ] == YES)
{
//create file handle
NSFileHandle *file;
file = [NSFileHandle fileHandleForReadingAtPath:#"/var/folders/y3/jv117_75505fnk8htdrs0qm40000gr/T/com.aprivacy.xmlCorePort.properties"];
//read data into file in NSData format
NSData *filedata;
filedata = [file readDataToEndOfFile];
NSLog(#"fileDATA = %#", filedata);
//convert NSData to NSString
NSString *string;
string = [[NSString alloc] initWithData:filedata encoding:NSASCIIStringEncoding];
NSMutableString *directoryPath1 = [NSMutableString stringWithString: #"share1>"];
[directoryPath1 appendString: string];
NSData *dataToWrite3 = [directoryPath1 dataUsingEncoding: NSUTF8StringEncoding];
NSFileHandle* outputFile = [NSFileHandle fileHandleForWritingAtPath:#"/Users/yp/Downloads/a.txt"];
[outputFile seekToEndOfFile];
[outputFile writeData:dataToWrite3];
//convert from string to array
NSArray *lines = [string componentsSeparatedByString:#"="];
NSLog(#"arrau = %#", lines);
//take one of the string and store it in sword
NSString *sword = [lines objectAtIndex:1];
NSLog(#"port : %#", sword);
int port1=[sword intValue];
Communicator *c = [[Communicator alloc ]init];
c.host=#"http:127.0.0.1";
c.port=port1;
[c setup];
}
else
{
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:#"Error"];
[alert setInformativeText:#"You are not logged in.Kindly login to start performing the operations"];
[alert setAlertStyle:NSWarningAlertStyle];
[alert runModal];
}
}
The above code, on the action performed first tries to check if the file is present at the /var/folders/y3/jv117_75505fnk8htdrs0qm40000gr/T/com.aprivacy.xmlCorePort.properties location or not.
This works perfectly fine , If the file is present , it shows a popup alert (which happens).
But if the file is present , it goes inside the if condition and tries to read the file where it fails .It always prints a blank string showing that nothing is being read.
So then I went and checked the entitlements in App Sandbox.
I tried to add an entitlement named com.apple.security.temporary-exception.files.absolute-path.read-only with a string value set to /var/folders/y3/jv117_75505fnk8htdrs0qm40000gr/T/com.aprivacy.xmlCorePort.properties so that it gets the permission to read the file from this location but still it doesn't solve my problem.
Could anyone please suggest how to get this file reading permission accessible in my app because the same code works completely fine in a newly created test project.
Following steps : Original client app running -login with user name and password Once logged in -it writes the port in a file At the same time ,once you are logged in with your application , if now you right click on any file in your system you will see certain extra extensions like share ,grant access etc. (This is because a finder project used to add extensions is merged with the original client) Now when I click on say share (on right clicking a file) , I want an action to be performed.The logic for action is written in (IBAction)Share (id) sender method This app used to add extensions is sandboxed because of which the permissions are restricted. So while I clicked on share , my logic was to read that file ,get the port and then connect to server using that port. I want to do everything inside action but I am unable to do so . It is not able to find the file data from /var/folder/y3/jv117_755fdlvfldsvgr/T/com.aprivacy.xmlcorePort.properties
Sandboxed apps (all in iOS) are only allowed access to specific directories. Use NSSearchPathForDirectoriesInDomainsto obtain paths to available directories.
Ex:
Objective-C:
NSArray *documentDirectoryPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:fileName];
NSError *error;
BOOL status = [string writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&error];
if (status == NSError) {
NSLog(#"error: %#", error)
}
Swift:
let filePath = "path/file.txt";
let documentDirectoryPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first as! String
let path = documentDirectoryPath + filePath
Note: Sandboxed paths is not consistent across clean builds.
Don't use absolute paths in sandboxed applications.
In OS X there is the NSTemporaryDirectory() function to have access to the temporary directory for this specific application in the container. Entitlements are not needed.
From the documentation
Some path-finding APIs (above the POSIX layer) refer to app-specific
locations outside of the user’s home directory. In a sandboxed app,
for example, the NSTemporaryDirectory function provides a path to a
directory that is outside of the user’s home directory but specific to
your app and within your sandbox; you have unrestricted read/write
access to it for the current user. The behavior of these path-finding
APIs is suitably adjusted for App Sandbox and no code change is
needed.
Source: App Sandbox in Depth

URL Containing forward slash doesn't work with NSWorkSpace

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];

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.

Get directory path from full filepath

New to objective-c. How do I get the directory from a full file path?
so if I have
/User/Test/Desktop/test.txt
I want
/User/Test
Also is there an equivalent to Path.Combine from .NET in Objective-C?
-[NSString pathComponents] returns an NSArray of path components.
For instance
NSString *path = #"/User/Test/Desktop/test.txt";
NSArray *components = [path pathComponents];
NSLog(#"%#", components); //=> ( /, User, Test, Desktop, test.txt )
Then you can take only the components you need and build a new path with them, for instance
NSString *newPath =
[NSString pathWithComponents:
[components subarrayWithRange:(NSRange){ 0, components.count - 2}]];
NSLog(#"%#", newPath); // => /User/Test/
Read the Working with Paths section of Apple's NSString documentation, you will find methods that answer your questions. For example, stringByDeletingLastPathComponent removes the last component of a path leaving the containing directory.

Embedding XML to iOS application

I'm making my first game in iOS and I need to load some levels that are stored as XML. Level files are stored locally and everything works fine from emulator. However, since XML is loaded in the runtime when i try to test my game on an actual device it can't find the XML files since they are not actually part of the app.
I'm coming from Flash background so I might have a wrong idea how this is done on iOS but I need to somehow bundle those XML files with the app, or embed it in the code somehow? Is this possible?
Thanks a lot :)
Well The code to look up your app bundle for the specified file is
NSString *path = [[NSBundle mainBundle] pathForResource:#"fileName.xml" ofType:nil]
NSURL *xmlURL = [NSURL fileURLWithPath:path];
[[NSXMLParser alloc] initWithContentsOfURL: xmlURL]
Hope this helps you...
You could add the XML file to your project and run time read it up with something like this
NSArray *myPathList = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *myPath = [myPathList objectAtIndex:0];
NSError **err;
myPath = [myPath stringByAppendingPathComponent:fileName];
if([[NSFileManager defaultManager] fileExistsAtPath:myPath])
text = [NSString stringWithContentsOfFile:myPath encoding:NSUTF8StringEncoding error:err];
You could consider using JSON in instead of XML. At least to my knowledge the available XML parsers are not nearly as simply to use as SBJson for instance (https://github.com/stig/json-framework/)
Here is my solution in Swift 3.0. In addition to the code, you'll need to add your xml file as a data set in your Assets.xcassets. Do this by creating a new Data Set, giving it any name, and then dragging the xml file from the finder to your newly created data set. When you reference the file in your code, you'll use the file name of the file, and not the name of the data set.
var parseResults = false
if let path = Bundle.main.path(forResource: fileName, ofType: ".xml") {
let url = URL(fileURLWithPath: path)
if let xmlParser = XMLParser(contentsOf: urlToFile) {
xmlParser.delegate = self
parseResults = xmlParser.parse()
}
}