NSUserDefaults of different application - macos

I am currently developing System Pane and my app have some configuration settings saved to User Defaults:
NSUserDefaults *userDefault=[NSUserDefaults standardUserDefaults];
NSData *encodedObject = [NSKeyedArchiver archivedDataWithRootObject:listOfStuff];
[userDefault setObject:encodedObject forKey:#"myStuff"];
[userDefault synchronize];
Can anyone tell me if and how a different application can read settings that have been saved in above System Pane?
Thank you.

The way to read someones preferences is very simple and straight forward:
NSUserDefaults *defaults = [[NSUserDefaults alloc] init];
[defaults addSuiteNamed:#"com.apple.systempreferences.plist"];
NSLog(#"DefaultExposeTab is: %#", [defaults stringForKey:#"DefaultExposeTab"]);
Make sure to initialize NSUserDefaults following way:
[[NSUserDefaults alloc] init];
then you can add desired preference list, in our case I would like to read System Preferences:
[defaults addSuiteNamed:#"com.apple.systempreferences.plist"];
and finally get value for whichever key you want, in this example:
"DefaultExposeTab"
Above example works like a charm. Please remember it will only work for current user.
Thanks.
P.S: Please note - above example will NOT work for sandboxed application.

As Melr explained, you need to use the suite name. But it's actually possible to read the preferences of another application from a sandboxed app. It requires a temporary exception entitlement.
For example, to read the Finder preferences, you need to add this to your
entitlements file:
<key>com.apple.security.temporary-exception.shared-preference.read-only</key>
<array>
<string>com.apple.finder</string>
</array>
Then you can access the Finder preferences like this:
NSUserDefaults *finderDefaults = [[NSUserDefaults alloc] initWithSuiteName:#"com.apple.finder"];
NSDictionary *standardViewOptions = [finderDefaults dictionaryForKey:#"StandardViewOptions"];
NSNumber *fontSize = [standardViewOptions valueForKeyPath:#"ColumnViewOptions.FontSize"];
If you need to read the preferences of another app, just replace com.apple.finder with the appropriate bundle identifier.

My app Prefs Editor uses the CFPreferences functions for this (e.g. CFPreferencesSetAppValue). That gives you direct control over whose app's settings you'll access.
You can even access the prefs of sandboxed applications with this method, if you know their plist file's full path and pass that to the applicationID parameter.
For more info, read through the answers of this question: How does OS X's defaults command get access to prefs of sandboxed apps?

Yes another application can read the settings if you save by using NSUserDefaults because it saves the settings as an unencrypted plist file on disk.
Reading another application's settings from a plist file is easy. Search for the file path and open it. NSDictionary can be initialized with a string of the file path to a plist file.
If the defaults you want to store are secret (like password, security tokens, etc), then add an encrypted item to the keychain instead of storing them in a plist.
https://developer.apple.com/library/mac/documentation/security/Conceptual/keychainServConcepts/01introduction/introduction.html

Related

How to save downloaded files(audio, doc) automatically to user's Downloads folder in mac os x sandboxed application?

In my Mac OS X application I tried to save downloaded files to application's directory(i.e. HomeDirectory()/Documents) but App Store rejected my application saying that your downloaded file is not accessible to the user easily( i.e. without opening the app). Then I tried to write the downloaded files to ~/Downloads folder by adding Read/Write permission in entitlements, but App Store again reject the application saying
Your application accesses the following location(s):
~/Download
The majority of developers encountering this issue are opening files
in Read/Write mode instead of Read-Only mode, in which case it should
be changed to Read-Only.
Other common reasons for this issue include:
creating or writing files in the above location(s), which are not valid locations for files to be written as stated in documentation.
writing to the above location(s) without using a valid app-id as a container for the written files.
Now the issue is App Store is neither allow me to save the files in App's Directory nor in System's folder(i.e. Downloads). Also I Don't want to use NSSavePanel every time. I want to download the files silently. Where should I save my files?
With the help of Security-Scoped Bookmark, user-selected read-write entitlement and NSOpenPanel I was abled to read/write to user selected folder.
Below are the steps I followed,
Added
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.files.bookmarks.app-scope</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
in Entitlements file.
Asked the user to select(or create and select) the desired folder to which my application want to access(read/write) using NSOpenPanel.
When user selects the folder, I created the bookmark of selected folder path as bookmarked path in NSUserDefaults using NSURLBookmarkCreationWithSecurityScope.
NSOpenPanel *openDlg = [NSOpenPanel openPanel];
[openDlg setCanChooseDirectories:YES];
[openDlg setCanCreateDirectories:YES];
[openDlg setAllowsMultipleSelection:FALSE];
[openDlg setPrompt:#"Select"];
if ( [openDlg runModal] == NSModalResponseOK )
{
NSURL *url = openDlg.URL;
NSError *error = nil;
NSData *bookmark = [url
bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope
includingResourceValuesForKeys:nil
relativeToURL:nil
error:&error];
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:bookmark forKey:#"DOWNLOAD_FOLDER_BOOKMARK_PATH"];
[userDefaults synchronize];
}
Once you saved the bookmarked path in NSUserDefaults you can access the saved path later using NSURLBookmarkResolutionWithSecurityScope.
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSData * bookmarkedPathData = [userDefaults objectForKey:#"DOWNLOAD_FOLDER_BOOKMARK_PATH"];
NSURL* urlFromBookmark = [NSURL URLByResolvingBookmarkData:bookmarkedPathData
options:NSURLBookmarkResolutionWithSecurityScope
relativeToURL:nil
bookmarkDataIsStale:nil
error:&error];
Once you got the saved Bookmarked URL you can use that URL to perform read, write operation. Before reading/writing from/to the URL, please start the scope using [urlFromBookmark startAccessingSecurityScopedResource]; . And after finishing read/write operation stop the scope using [saveFolder stopAccessingSecurityScopedResource];
Note: I did tried to write to directly to Documents, Downloads, Desktop without creating folder inside these directories but Apple rejected the app, saying
Your application access the following locations 'Downloads'.
Then instead of writing directly to these directories( Documents, Downloads, Desktop), I asked the user to select(create & select) a folder, then performed the read/write operations on user selected folder using Security-Scope-Bookmark.
Hope this helps someone.

iOS8: Sharing common files between apps

Two iOS: AppA and AppB
Both Apps are created by me and I would like to share one single file between both of the apps.
i.e. AppA launches on deviceA and the User saves data on fileA. Later the User launches AppB on the same (deviceA) and also saves data on fileA. Both apps are saving data on the same file.
I'm aware that I can use NSUserDefaults and share Keychain between apps, but that's not what I'm looking for.
I read up on document extensions provider and app groups, but I'm confused if I can use these for this scenario? Or is there any other way to accomplish this?
You can do it using Application Group Container Directory:
NSFileManager *fm = [NSFileManager defaultManager];
NSString *appGroupName = #"Z123456789.com.example.app-group"; /* For example */
NSURL *groupContainerURL = [fm containerURLForSecurityApplicationGroupIdentifier:appGroupName];
NSError* theError = nil;
if (![fm createDirectoryAtURL: groupContainerURL withIntermediateDirectories:YES attributes:nil error:&theError]) {
// Handle the error.
}
You could just upload the files after saving to your server and make both apps request updates for the file whenever they are launched.
Hope that helps :)

NSWorkspace throws error when launch the mail application

I tried the below code, it is working fine for me. Also am able to launch the mail application as well.
//Note the below path is coming from bundle identifier of Mail APP
NSString *path=#"/Applications/Mail.app"
NSURL *mailURL = [NSURL URLWithString:path];
NSError *err=nil;
[[NSWorkspace sharedWorkspace] launchApplicationAtURL:mailURL
options:NSWorkspaceLaunchDefault
configuration: someData
error:&err];
But it throws me the below error message on console, What it means actually. How to resolve the below issue.
CFURLCopyResourcePropertyForKey failed because it was passed this URL which has no scheme: /Applications/Mail.app
The error is thrown because you are not creating a valid URL. The URL needs a scheme, in your case it is file: so the correct URL is file:///Applications/Mail. You need to create a file URL which works as follows:
NSString *path=#"/Applications/Mail.app";
NSURL *mailURL = [NSURL fileURLWithPath:path];
Note that your code breaks if the user ha moved Mail.app to another location. Also note that if the user doesn't use Apple's Mail app, it won't work well for the user either.
One possibility of doing it a more correct way is given here: How to launch New Message window in Mail.app from my application
Another option is to get the URL for Mail.app in a more fleixble way covering for users that have moved Mail.app. The idea is to use the bundle identifier and then ask NSWorkspace to launch this application by using
- (BOOL)launchAppWithBundleIdentifier:(NSString *)bundleIdentifier
options:(NSWorkspaceLaunchOptions)options
additionalEventParamDescriptor:(NSAppleEventDescriptor *)descriptor
launchIdentifier:(NSNumber **)identifier
(see also in detail here http://theocacao.com/document.page/183).

In App Purchase How to check if item purchased

hi there i have followed a tutorial online for in app purchase and have gotten it to work perfect but the only problem is when i reopen the app i have to restore the purchase to make is display again
how would i check when that app loads that the item was purchased and can display the purchased content
here is the link for the online tutorial - http://www.techotopia.com/index.php/An_iPhone_iOS_6_In-App_Purchase_Tutorial
if anybody could give me some pointers that would fantastic
You can store those data in NSUserDefaults or in the keychain and check in those whether the user has already purchased the item or not
To Store in the NSUserDefaults:
when user made your in App then set value in NSUserDefaults like this
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setBool:YES forKey:#"isPurchase"];
[userDefaults synchronize];
and when you starts the app you can check whether the user has already purchased the in-app like this:
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
if(![userDefaults boolForKey:#"isPurchase"])
{
// Unlock your features code comes here
}
And I strongly recommend you to go through this tutorial

Cocoa equivalent of .NET's Environment.SpecialFolder for saving preferences/settings?

How do I get the reference to a folder for storing per-user-per-application settings when writing an Objective-C Cocoa app in Xcode?
In .NET I would use the Environment.SpecialFolder enumeration:
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
What's the Cocoa equivalent?
In Mac OSX application preferences are stored automatically through NSUserDefaults, which saves them to a .plist file ~/Library/Preferences/. You shouldn't need to do anything with this file, NSUserDefaults will handle everything for you.
If you have a data file in a non-document based application (such as AddressBook.app), you should store it in ~/Library/Application Support/Your App Name/. There's no built-in method to find or create this folder, you'll need to do it yourself. Here's an example from one of my own applications, if you look at some of the Xcode project templates, you'll see a similar method.
+ (NSString *)applicationSupportFolder;
{
// Find this application's Application Support Folder, creating it if
// needed.
NSString *appName, *supportPath = nil;
NSArray *paths = NSSearchPathForDirectoriesInDomains( NSApplicationSupportDirectory, NSUserDomainMask, YES );
if ( [paths count] > 0)
{
appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:#"CFBundleExecutable"];
supportPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:appName];
if ( ![[NSFileManager defaultManager] fileExistsAtPath:supportPath] )
if ( ![[NSFileManager defaultManager] createDirectoryAtPath:supportPath attributes:nil] )
supportPath = nil;
}
return supportPath;
}
Keep in mind that if your app is popular you'll probably get requests to be able to have multiple library files for different users sharing the same account. If you want to support this, the convention is to prompt for a path to use when the application is started holding down the alt/option key.
For most stuff, you should just use the NSUserDefaults API which takes care of persisting settings on disk for you.

Resources