Objective C - How to determine if a folder is a package? - objective-c

App Packages are basically folders and you can read their contents just like any folders. I understood this.
My question is:
If I have a folder called folderA which has files and a package called PackageA which has files, how can I programmatically tell what is a normal folder and what is a package ?
In my application I need to exclude any packages but process any normal folders.
Any advise would be appreciated.
I have tried to check via NSBundle but both normal folder as well as package folder is an NSBundle. I was hoping one is not. I am sure there is a property that will tell me that?
I solved it as:
NSNumber *isPackage;
NSError *error = nil;
[filePath getResourceValue:&isPackage forKey:NSURLIsPackageKey error:&error];
if( [isPackage integerValue] == 1)
{
// treat as package
}

There are multiple ways to do this, the "modern" way is to use NSURL's getResourceValue:forKey:error: method (or one of its siblings). The key for determining whether the NSURL instance references a package is NSURLIsPackageKey.

Related

Standard temporary directory in OSX

I'm trying to figure what is the best place where to store temporary files in the OSX version of my application.
The obvious answer /tmp is not good since it is cleaned up at boot and my application may need to continue an interrupted job also after a restart.
I tried also to use the path pointed by the environment variable TMPDIR, that is the same returned by NSTemporaryDirectory(), that changes every boot and is something like:
/var/folders/wx/p4rqqs8d1ws0wlpx9dkwsh_80000gn/T/
.. but also the contents of this path are removed at boot.
There is a standard path where I can place some temporary files, resilient to restarts, or I have to invent my own solution (ie ~/Library/myapplication/temp)?
In Windows I'm using GetTempPath() and it works the way it should.
I've found my answer googling harder that I did before asking here, in this excellent article:
https://www.cocoawithlove.com/2009/07/temporary-files-and-folders-in-cocoa.html
Reading the articles and the various options I found that the Caches directory (NSCachesDirectory) is the correct place where to store my files. Placing them in "Application Support" will cause them to be backed up by time machine.
So here is what I did:
const char *get_temporary_dir()
{
NSString *path = nil;
NSString *bundleName = [[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleIdentifier"];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
if ([paths count]) {
path = [[paths objectAtIndex:0] stringByAppendingPathComponent:bundleName];
} else {
path = NSTemporaryDirectory();
path = [path stringByAppendingPathComponent:bundleName];
}
return [path UTF8String];
}
... I'm not sure if the fallback to the standard, deletable, directory is needed but it doesn't hurt!
You should use the "application support directory" - this is typically ~/Library/Application Support or for a sandboxed application and equivalent within its container.
To obtain the URL for this directory you use URLForDirectory:inDomain:appropriateForURL:create:error: passing as first argument NSApplicationSupportDirectory.
Within this directory you need to create a directory just for your application, using your app's bundle ID is a common strategy for naming this directory.
This directory is intended to store files needed by your application, but not your user's files.

How do to identify that a file is a package?

I have used NSDirectoryEnumerator to enumerate files in a Cocoa App (OSX). I am able to identify many properties of files e.g that a file is a directory or not, its creation date,modification date etc using NSURL File Property Keys. What I am not able to identify is that whether a file is a package or not ? I have gone through every bit of NSURL,NSFileManager reference but I have not been able to find a way. Am I missing something ?
I have also seen that for a file that is a package NSURLIsDirectoryKey returns true. But in my case I still want to categorise directories and packages separately.
I found the method in NSWorkspace class. wonder why it was not included in NSURL File Property Keys
- (BOOL)isFilePackageAtPath:(NSString *)fullPath
Return Value
YES if the path identifies a file package; otherwise, NO if the path does not exist, is not a directory, or is not a file package.

Obj-C OS X Delete application from /Applications

I am writing an application to remove a piece of software and its files from OS X. I can delete everything else except the .app and a folder in /Applications. I am using AuthetificationExecuteWithPriviledges, I know that it is deprecated, so could that be giving me admin issues when attempting to move the .app to the trash bin? When I step through the process I see an NSCocoaErrorDomain, with code 513. I did a quick search for it, and saw it is related to a permissions error. My other theory is that my path to the application is incorrect. I am using:
NSString *ibmNotesApplication = #"/Applications/IBM Notes.app";
If you are wondering why I am trying to delete Lotus Notes, it is because the Notes uninstaller provided by IBM doesn't have all of the functionality I would like. So, I am writing one that removes notes and all files related to it so we can have a fresh install.
Here is a snippet of code where I move an item in the array to the trash bin.
if ([filemanager fileExistsAtPath:object])
{
NSURL *objectURL = [NSURL fileURLWithPath:object];
NSURL *trashedObject;
NSError *error;
//Moves item to trash bin and update log
if([filemanager trashItemAtURL:objectURL resultingItemURL:&trashedObject error:&error])
{
NSLog(#"Trashed %# to %#",objectURL, trashedObject);
self.textfield.stringValue = [NSString stringWithFormat:#"%#%#%#%#%#%#", self.textfield.stringValue, #"Trashed ", objectURL, #" to ", trashedObject, #"\n"];
}
As you mentioned in the question, the function AuthorizationExecuteWithPrivileges is a deprecated function and has been for quite a while now. I suggest you start by following Apple's advice of factoring out the privileged process into a separate privileged helper tool, which is launched with the help of launchd.
You can follow the example code here.
If the permission problem still exists, then at least you've gone about refactoring your code to the right way of doing things, knowing that it will be supported for future versions of the OS.

Retrieve info.plist file from command-line tool

Using the following steps, I'm able to embed an info.plist into a command-line tool.
http://developer.apple.com/library/mac/#documentation/Security/Conceptual/CodeSigningGuide/Procedures/Procedures.html
I know how to retrieve the plist file from a .bundle, but I'm not sure how to do the same in a single-file tool like I've got.
I've embedded the info.plist into the command-line tool so that I can store the version in it. Does anyone know how I can retrieve it at run-time so I can determine what version is running?
Thanks
__info_plist is a "magic" section name that makes the following Just Work:
NSBundle *bundle = [NSBundle mainBundle];
id version = [bundle objectForInfoDictionaryKey: (NSString*) kCFBundleVersionKey];
NSLog(#"mainBundle.version = %#", version);
If you need to read a bundle embedded in a different executable than the one currently running, this answer by Bavarious from the comments has a more comprehensive list of approaches.

Problem writing plist in local vs. admin mode

In my application, I am storing user preferences (which are applicable for all users) into a plist file,
which is attached as a form of bundle.
The problem is, it runs fine in admin mode, but when I run the application, it's not writing the file. Do I need to set some attribute to write to the plist in local mode? Or is it not possible at all?
My code for writing the file is below:
-(void)SavePrefrence:(NSString *)fileName PrefrenceOption:(NSMutableDictionary *)pDict{
NSString *filePath = [[[NSBundle mainBundle] pathForResource:fileName ofType:#"plist"] retain];
NSDictionary *pTemp = [[NSDictionary alloc]initWithDictionary:pDict];
bool bRet = [pTemp writeToFile:filePath atomically:YES];
if(bRet==YES){
NSLog(#"File Saved ");
}
else {
NSLog(#"File not saved ");
}
}
This is the code which calls it:
-(void)SaveListSettings:(NSMutableDictionary *)pListSettings{
[ self SavePrefrence:#“MyList" PrefrenceOption:pListSettings];
if(pListInfo)
[pListInfo release];
[self LoadListProfile];
}
The application bundle will, by default, only be writable by the owner (if it's installed by dragging into the Applications folder, this'll be whoever installed it; if it's installed by a .pkg, it should probably be root). The way you're doing this, if I follow it properly, requires a user to have write access to the app's Contents/Resources folder, which is a really bad idea (not that it's unheard of -- see the University of Utah's documentation about "Poorly-Made Applications" for examples). Actually, saving preferences inside the application is a bad idea anyway; that's what the various Library/Preferences folders are for (~/Library/Preferences for personal settings, /Library/Preferences for system-wide settings. But, of course, /Library/Preferences is only writable by admins (for very good reasons). I don't really know of a good way to handle this, as system-wide settings modifiable by non-admins is not exactly normal.
You could use an installer which asks for an admin password and then create "/Library/Application Support/MyApp" and then either make this world writable, or make a sub-folder inside it which is world-writeable. Now MyApp running under a non-admin account can still write to this shared folder.
If you don't want to make the folder world-writeable then include a helper app to the bundle to do the writing and make the helper setuid root by using an installer which asks for an admin password...
BTW: Both of those options will fail Mac App Store rules. Maybe you can use '/Users/Shared', but I don't know if it is allowed by MAS, and anyway it is far from standard. Which would leave you with storing it on a web server...