iOS SDK: Working with NSUserDefaults - objective-c

With the NSUserDefaults class, you can save settings and properties related to application or user data.
now i have tow question :
( settings and properties ) Where are stored ?
if =>The objects will be saved in what is known as the iOS “defaults system"
then : What is iOS “defaults system"
How long will remain stored ?

Where? It depends on what kind of app you're developing. For AppStore applications, in the sandbox folder of your installed app folder, more precisely in the Library/Preferences folder. The actual file that contains the settings is a PLIST (property list) file whose name matches the bundle identifier of your app.
All in all:
(/var/mobile/Applications/XXXX-XXXX-XXXXXX-XXXXXXXX/MyApp.app/Library/Preferences/com.mycompany.bundleid.plist
(where XXXX etc. is an UUID).
If you're making an app for jailbroken devices (i. e. a Cydia app), it will be a plist file again, whose name matches the bundle ID, but it will be installed in
/var/mobile/Library/Preferences/com.mycompany.bundleid.plist
The iOS defaults system is the part of the Foundation framework, iOS itself and the files and directories altogether which manage, store and modify your preferences/settings/defaults.
How long? Again, it depends on what kind of app you are writing. For a normal AppStore app, the defaults are persited to the file - but only as long as the user doesn't delete your app. When your app is deleted, the whole sandbox, app bundle and od course the user defaults will also be gone. Forever, irreversibly.
If you develop a jailbroken application which resides in the system apps' directory (/Applications), then the user defaults file will be available in /var/mobile/Library/Preferences even if the user deletes your package/application, as APT (the backing package manager of Cydia) only makes note of the files your package originally contained, and that's not the case with the defaults property list file (which is only created when your app first accesses the NSUserDefaults class.) If the user manually deletes the file or restores to a stock firmware, it'll be gone.

Answers:
In the application's sandbox. Where exactly is unimportant as you aren't going to be accessing the defaults directly anyway.
As long as your app is on the device. If you delete the app, the sandbox is deleted as well, so that's when they get deleted. If you update your app, the same sandbox is used, so they don't get deleted.
As for what it is, I'm just going to quote you a bit of the documentation:
The NSUserDefaults class provides a programmatic interface for interacting with the defaults system. The defaults system allows an application to customize its behavior to match a user’s preferences. For example, you can allow users to determine what units of measurement your application displays or how often documents are automatically saved. Applications record such preferences by assigning values to a set of parameters in a user’s defaults database. The parameters are referred to as defaults since they’re commonly used to determine an application’s default state at startup or the way it acts by default.
At runtime, you use an NSUserDefaults object to read the defaults that your application uses from a user’s defaults database. NSUserDefaults caches the information to avoid having to open the user’s defaults database each time you need a default value. The synchronize method, which is automatically invoked at periodic intervals, keeps the in-memory cache in sync with a user’s defaults database.

Related

Monitoring a folder in sandboxed application

How would I monitor the users Trash so that when a file is added I can send an action.
Thanks in advance!
A sandboxed application cannot access the Trash. Sandboxed applications are sandboxed for security reasons and in the Trash may be all kind of documents a user has deleted and that a sandboxed application would have never had access to in their former destination. Of course a user expects that those files are still save once they are put into the Trash.
If it really makes sense for your app to access the Trash, e.g. because it is a Trash Management app (stupid example, I know), Apple will still grant your application access to the Trash, if you only ask for it. Therefor you need to specify a Temporary Exception Entitlement, either of the type
com.apple.security.temporary-exception.files.home-relative-path.read-only
if read-only access is enough for you or
com.apple.security.temporary-exception.files.home-relative-path.read-write
if you also need write access. Those are the keys in the entitlement dictionary and the value is an array of strings, each string specifies a directory. In your case they array would contain /.Trash, which translates to /Users/<username>/.Trash for the current user. E.g.:
<key>com.apple.security.temporary-exception.files.home-relative-path.read-write</key>
<array>
<string>/.Trash</string>
</array>
This will allow your app to be partially freed from its sandbox, as long as it only accesses this directory (or its sub-directories).
Note that Apple will only approve an app that uses one of the Temporary Exception Entitlements under certain conditions which are listed on the App Store Connect webside (you must login to see that side and you need an Apple ID with permission to use App Store Connect).
AFAIK those conditions include that you must file a bug report with Apple's Feedback Assistant (again, you must log in to the webside, but this time every Apple ID is allowed to log in) and you must explain Apple why you need this exception as otherwise your app cannot do whatever it is supposed to do. You better be good at explaining that! If Apple doesn't consider your use case legitimate, they will close this bug as invalid and then your app submission will be rejected. If the bug is considered valid, you must tell Apple that your app uses a temporarily entitlement when you submit the App and you must point Apple to the bug report you have created. They will read it again and if the store team agrees with the development team that this is a valid reason, they will allow your submission to pass through.
Once your app has the appropriate entitlement, you should be able to monitor Trash the same way as a not sandboxed app can do so. E.g. using the FSEvent API. With the function FSEventStreamCreate() you can create a FSEventStreamRef that monitors a certain path (in your case the Trash), calls a user defined callback function whenever the contents at this path changes and can be scheduled on a RunLoop. Once scheduled, whenever the content of the directory changes, the callback function is called. In the callback function you may do whatever you want to do, e.g. to get a higher level event (since the callback function is only plain C, so you may want to post a NSNotification that you can handle in your Obj-C code, e.g. in your UI code) or maybe directly call an Obj-C method of some object (stored in the callback context, a global variable or which is accessible as a singleton, etc.)

Reading NSUserDefaults from helper app in the sandbox

I found some resources on reading the NSUserDefaults of another application.
Objective-C NSUserDefaults caching prevents another app from accurately reading changes
NSUserDefaults: Is it possible to get userDefaults from another app?
Apparently, it's not possible.
However the questions firstly relate to iOS, and secondly the two apps are completely different.
I have a LaunchAtLogin helper app. But it does some other tasks too.
Therefore, the helper app should run always, but only start the main app if the BOOL in the NSUserDefaults is set.
Is there a way I can achieve that?
Since 10.7.4 you can use Application Groups within the sandbox. All applications within the group share the same sandbox. See Application Groups on how to set these up.
It's possible to share preferences between a main app and helper app using Security Application Groups and -[NSUserDefaults initWithSuiteName:]:
Security Application Groups
In order for multiple apps to share a common container, you'll want to set the com.apple.security.application-groups entitlement (in your main and helper app) to a common identifier, such as #"com.company.my-app-suite". See Adding an App to a Group for more information.
User Defaults Suites
As per the Foundation Release Notes for OS X 10.9:
For applications that are part of a Security Application Group, the NSUserDefaults "suite" APIs (-initWithSuiteName:, -addSuiteNamed: and -removeSuiteNamed:) will operate on a suite shared by applications in the group and stored in the group container, if the suite identifier is the identifier of the group.
So you'll want to do something like this in your application delegate (or similar):
- (NSUserDefaults *)sharedUserDefaults {
static NSUserDefaults *shared = nil;
if (!shared) {
shared = [[NSUserDefaults alloc] initWithSuiteName:#"com.company.my-app-suite"];
}
return shared;
}
And use that instead of [NSUserDefaults standardUserDefaults] throughout both your apps.
Apps can share a container directory on iCloud.
From Apple's doc on configuring your iCloud entitlements:
The iCloud Containers field identifies the list of container directories that your app can access in the user’s iCloud storage. (This field corresponds to the com.apple.developer.ubiquity-container-identifiers entitlement.) The strings you add to this list must correspond to bundle identifiers for apps created by your team. Xcode uses the current app’s bundle identifier to specify the first string; you can change this to a different bundle identifier if you want multiple apps to share a main container directory. You can also add additional bundle identifiers for your team’s other apps.

Can I programmatically wipe the application data in applicationDidFinishLaunching:withOptions:?

For testing purposes, I'd like to be able to just reset the application to a clean state. (Similar to what deleting the app from the Simulator / iPhone does).
Assume we have a WIPE_DATA define, if that is set, the app should start as if it has been just installed. Obviously if you know the app, you know where it stores data in NSUserDefaults etc. I was wondering if there was a more generic approach that requires no insight into the specific app and thus would be applicable to any app.
In your app you have two kinds of data - NSUserDefaults settings and files in Documents directory. If you want to wipe all the data to make your application to initial state and settings, you should create e.g. an app delegate method which set initial NSUserDefaults settings and clean application generated files in Documents directory.
As far as I know there is no standart system way to do that.

Mac App: Storing Information w/ Document-based Applications

I'm in the process of planning out the infrastructure for a Mac App, and we have a startup screen with many user files listed. We want the App to be iCloud-compatible (thus the need for Document-based (key-value won't cut it since they aren't nested - correct me if I'm wrong here)). Essentially, we don't want to have the user keep track of each individual file themselves as that would be irritating, but rather store it in the App's folder until the user needs it (i.e. Email, Export, etc). It would eliminate a lot of the friction in the app, we think. I guess my question is:
Is it possible to store files automatically in the App's installation folder (or somewhere locally?) without bothering the user - in a Document-based app - and still be App Store compatible? Seems like the ideal solution - user opens app, App knows it's save location and automatically saves documents there when a user creates one, and pulls them to share if needed. Any help?
Yes, it's possible. You won't want to store document's in the application's installation folder. For one thing that'll violate the App Store rules, but it's bad behavior anyway, since Applications are normally installed in /Applications, which shouldn't be cluttered up with other files. So storing things in ~/Library/Application Support/YourAppName is the way to go.
To actually implement this, take a look at NSDocumentController and NSDocument itself. You'll basically want to override/modify any UI that allows users to choose a location to save/open documents. Instead, just let them name the documents, and then automatically save them with the given name in the app support folder. Then create a UI that allows them to browse and open those files within the app.

Changing provided services based on user preferences in OSX?

I would like to be able to change the OSX services that my application provides based on the current user's preferences (like adding more, changing the name,...). This basically means modifying the Info.plist (NSService key), but I don't think it is a good practice when an application modifies its own Info.plist while running, right? (At least based on few searches here). Is there any other option how to get this functionality?
I guess it should always be an external entity who does modify the Info.plist? So far I can only think about providing a system preference bundle which will do the modification in the actual app? Do you have any ideas?
Thank you
One way would be to install a service in ~/Library/Services that provides the services, and edit that application's Info.plist from your main application.
Of course, that should be an explicit action, so the user (hopefully) knows to delete the service if they delete your application. And you should document that procedure on your product's support web page, just in case they don't.
Here's a small twist to the previous recommendations, create a separate app that handles the service and bundle it within your Resources. When you want to enable the service, instead of copying the file over to ~/Library/Services, create a symbolic link within the ~/Library/Services folder that points to the app you bundled in your Resources.
This way if the user deletes your application, all that will be left behind is a symbolic link pointing to an invalid location. Does less arm than actually leaving the app behind and will have the added benefit that the service will no longer be available (since the info.plist will have been removed when the user deleted your app).