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.
Related
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.
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.
I am creating an ios application with xcode 4.6.1 which uses core data to save data in the database, i have provided the deployment target as ios 4.3 and the base sdk is set to latest 6.1.
The place where i am stuck is when i run the application on ios 4.3 i see that my sqlite file is created but contains no tables in it. I have wrote no special code here and every thing is by default taken care in the app delegate, i came across few post where other developers have said to
NSURL *modelURL =[[NSBundle mainBundle] URLForResource:#"StudentData" withExtension:#"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
use "mom" above but i even did that and it seems that every time in ios 4.3 this issue pops up, i have also tried and reset my simulator lots of time but nothing seems to be working and i do require ios4.3 support so please let me know what's going on here and what do i need to do.
I also tried printing
NSLog(#"%#",[self managedObjectModel]);
but it's not nil
Please note their is no mistake in the name of the files here.
Thanks
NSBundle *modelWrapper = [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:#"StudentData" ofType:#"momd"]];
NSString *modelPath = [modelWrapper pathForResource:#"StudentData" ofType:#"mom"];
managedObjectModel = [[NSManagedObjectModel alloc]initWithContentsOfURL:[NSURL fileURLWithPath:modelPath]];
By default in Mac OS you don't have write permission.
From terminal go to the path where you have stored your StudentData file and then write this command chmod 777.
It will allow you to write data in files.
Well, i got this working what i did was ran the application and saw that it created the sqlite file but with no tables but when i tried to insert the data in the database with ios 4.3 then the data got inserted and tables were created surprisingly it did not make any sense to me but i got this working without changing anything in the code.
Well, the title is quite explicit, but a little explantations for those interested in the background.
I'm developing a little image browser. On part of the application is a directory browser which allows me to browse all the folders of my hard drive and mounted volumes.
And while profiling, I noticed that the most time consuming method of my application was the following piece of code :
// get the content of the directory
NSFileManager * fileManager = [NSFileManager defaultManager];
NSURL * url = [NSURL fileURLWithPath:mPath];
mCachedContent = [[fileManager contentsOfDirectoryAtURL:url
includingPropertiesForKeys:nil
options:NSDirectoryEnumerationSkipsHiddenFiles
error:nil] retain];
// parse the content, count the number of images and directories.
for (NSURL * item in mCachedContent)
{
if (CFURLHasDirectoryPath((CFURLRef)item))
{
++mNumChildren;
}
else if ([FileUtils isImage:[item path]] == YES)
{
++mNumImages;
}
}
This is necessary so that the NSOutlineView can know if a directory is expandable (and the number of images is also a feature I need)
To be more precise, the most time consuming method if [NSFileManager contentsOfDirectoryAtURL...]
So, is there any other way of getting a directory's content more efficient than the one I'm using ?
Thanks in advance for any help !
No matter how you write this function (e.g. with either Cocoa's NSFileManager API or the Unix opendir(3)/readdir(3) API), it's going to be I/O-bound—you're going to spend more time waiting on I/O than on any CPU operations performed in the middle layers.
If this is truly your bottleneck, then that means you're doing way too much I/O. Make sure you're not doing anything stupid like continually reading the contents of the same directory over and over again hundreds of times per second. If you need to continually watch a particular directory and take action whenever something in that directory changes (e.g. a file gets written to, a file is created or deleted, etc.), then use the File Systems Events API. This allows you to efficiently respond to those events when they happen without having to continually poll the directory.
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...