I am developing an app for learning piano and I would like to offer lessons as non-consumable in app purchases. All of the files that are required for the lesson will be bundled with the app (as they don't take up a ton or space; two PDFs, a .mid, and a .png). Currently, for testing purposes, I have just been hard coding the lessons into my app and they get loaded on viewDidLoad.
My question is how can I store a library of lessons that I can modify when a purchase is made to show that it had been purchased? Essentially, all I want to do is when a completed transaction is received, the value of purchaseStatus will change from 0 to 1.
The stored data consists of a number of strings, NSNumbers, and an array of NSNumbers.
I have seen a few options such as plists and NSUserDefaults but I'd really appreciate some advice on the best way to go about it.
Thanks in advance :)
I would say storing the purchase status of each purchasable module in an NSDictionary that you store in NSUserDefaults would probably be the most straightforward/simplest option. Every time you launch the app, query this dictionary to determine what has been purchased and accordingly reveal those purchased entities to the user (in the form of an entry in a table view, an icon, a no longer hidden button, etc.).
Plists are fair game as well, but it would require a bit more maintenance (where to store the plist so that it may be backed up by iCloud, etc.) whereas NSUserDefaults has that all taken care of for you (it's backed up by iCloud).
Related
The answer to this question may be iCloud storage, but I am using this for a very light-weight piece of data and think it may be extreme to use iCloud for this one purpose. We have an in-app subscription app. Before we provided the app for free but would not deliver any content to it until the user subscribed to content through the in-app subscription.
Some reviews indicate people think that we are trying to trick them by saying free and then saying they have to subscribe. We want the app to be listed as the price of the subscription to avoid this misunderstanding but after the first year has elapsed offer them the in-app subscription to continue. We plan to store the initial launch date in persistent storage so that we can determine when to begin requiring a subscription receipt.
1) We wanted to use the keychain storage since this information will persist even if a user removes the app from his/her device and then re-installs. The problem is if the user gets a new device and does not restore it from an encrypted backup, then the initialized value will be lost.
2) If we use our servers to store this value for them, then there seems to be no way to tie it to that appleID; so that is ruled out as a possibility.
3) Back to iCloud, it just seems like a lot to add iCloud support just for this, and I believe the user still has to have iCloud enabled for this to work.
Does anyone know the solution to this data persistence problem? Help is appreciated!
Yes, nsubiquitouskeyvaluestore is the way to get it synched with new devices. Using this in conjunction with both keychain for storing on a device after app as been removed and nsuserdefaults for quick and easy unconstrained access is the way to go. Apple also has a way to read receipts for the actual app purchase now, but it only works for iOS 7. Solutions should conditionally rely on receipt data from nsbundle.mainbundle url if it is available.
Are there any recommendations how to store in-app purchase results.
I made purchase using
raywenderlich tutorial
It stores the result to NSUserDefaults.
So the question : Is it safe to store the in-app result as a bool value in NSUserDefaults. Because everyone can look in Library/Preferences folder and see what is written there.
Yes, this should be OK, but it may depend on the app. Most "normal" users aren't going to be poking around in your Library/Preferences file. So if you're writing an app for a mainstream audience (average to low technical knowledge), NSUserDefaults is a safe place. You also have to consider people's time value. If your app costs 99 cents, it's probably faster to buy it than to go hack some prefs file. If your app costs $99, there's an incentive for some people to go looking for hacks.
So If you're writing a super expensive app, or an app for a hacker/developer audience, you may want to store your result in a Keychain or something more secure.
I have personally used a simple "hasUpgraded" BOOL stored in NSUserDefaults, and never had a problem.
I'm facing this problem while designing my iOS app. Suppose that a user purchases an app and downloads it to the iPhone. I would like to provide him with a default consumable item the first time he runs the app to use whenever he wants , however I would also like to track if the user has already consumed the item. This way if he decides to reinstall the app we can restore the transactions (if he used the item) or we can avoid possible intents to download different kind of content by reinstalling app and consuming default items each time. (Guess NSUserDefaults is not an option here).
One approach that came to my mind was using UDID(or any iOS 6 alternatives) to keep a record on server of the user's device the moment he uses the default item. But this will limit items just to the device from which they consumed content.
It would be great to support all the user's devices (like inAppPurchases), but I can't figure out a way to implement this.
Any suggestions or help would be great.
Thanks a lot.
In order to tie information to a user (not just a device she used at one time), you'll need to ask the user to identify herself and save it someplace other than the device. In other words, a backend that implements registration and login.
From scratch, this can be a lot of effort that an iOS developer didn't count on. Fortunately, there are several services in the world that provide a substantial head start. Here's a nice round-up. I've had direct experience only with Parse.com, and think it's excellent.
I want to create an app which holds sensitive information (imagine it's bank account details, thought it's not). The user enters this information on a form the first time the app starts up. I want this info to be saved, and available, any time the user uses the app (without having to enter a password). However, if the iPhone has a password lock on it, and is stolen, I don't want the data to be easily accessible from the file system.
What is the best way of encrypting or obfuscating the data? There is not a lot of data, just a dozen NSStrings from the UITextFields on the form.
I'm aware there are encryption export restrictions on the iPhone for non-US developers (I am in UK), so I would prefer to avoid going jumping through any of Apple's app submission hoops to get it on the store.
Why not use the built in Keychain Services? That's what it is for.
EDIT: There an article in SDKDevFAQ.com about Keychain Services that points to a tutorial and sample code on github. Also, check out this blog entry about using the Keychain.
I don't know if a jailbroken iPhone device lets you read NSUserDefaults from other applications or not. If not, you could just store your information in there instead of as a file.
Alternatively, you could generate some salt based on (but not equal to) the device ID, and simply XOR it with the bytes of the strings. As long as your algorithm to generate the salt isn't trivial and the strings aren't too long, the data will be fairly safe. Without getting into heavier encryption stuff, you can't guarantee too much more than "fairly safe".
Is this the case? Do NSUserDefaults get reset when you submit an update to an app on the App Store, or are they reset?
My app is crashing when updated but not crashing when downloaded fully - so I'm trying to determine what could possibly be different in the updated session to the freshly downloaded session.
Cheers,
Nick.
They are usually not reset unless the user deletes the app. For basic data, NSUserDefaults is the best way to save data such as preferences, dates, strings etc. If you are looking to save images and files, the file system is a better bet.
I beleive the answer is YES, it will persist. This also fully documented under the Application Directory chapter in the Apple iPhone OS Programming Guide.
Direct answer to the posted question: YES.
Your problem:
Your app gets crashed due to logic issues. Suppose you store an object in defaults and the app checks it's value on launch (or elsewhere). In you update you could change the way it is checked or used, e.g. you expect a value, but the object is nil, or vice versa. This may cause a SIGABRT or EXC_BAD_ACCESS.
If you had CoreData model and you changed something in your model and update, without managing migration, thats probably reason why your app crashes on update...
I have a similar experience. Our app stores a version number in Settings.Bundle/Root.Plist. This gets displayed through the iPhone Settings app. What we find is that on an Install the version number gets loaded from the app bundle - therefore the version number is correct. On an update however the version number doesn't change. This gives the impression the user is running a previous version of the app. We don't have any logic linked to the version number, it's just for display (it could be used by contact centre staff when diagnosing faults).
Our experience is NSUserDefaults doesn't get cleared when a user updates our app, but the Settings display doesn't get updated either.
Be aware of this case, when your app is running in background and you cannot access your stored values in NSUserDefaults:
Eric:
There have been many threads and bugs about this, but it's happening to me again in ios 9. I have an app that launches in the background in response to NSURLSession tasks and content-available pushes. Reproducibly, if I reboot my phone and wait for a background launch of my app to happen, then when I open the app I find that [[NSUserDefaults standardUserDefaults] dictionaryRepresentation] contains all the system values, e.g. AppleITunesStoreItemKinds, etc. but does not contain any of the values I have set. If I force-quit and relaunch the app all of my values come back. Is there any way to avoid it caching the "empty" standardUserDefaults from before the phone is unlocked, or at least to determine when they are messed up and fix them without having to force-quit the app?
Eskimo (eskimo1#apple.com):
The problem here is that NSUserDefaults is ultimately backed by a file in your app’s container and your app’s container is subject to data protection. If you do nothing special then, on iOS 7 and later, your container uses NSFileProtectionCompleteUntilFirstUserAuthentication, a value that’s inherited by the NSUserDefaults backing store, and so you can’t access it prior to first unlock.
IMO the best way around this is to avoid NSUserDefaults for stuff that you rely on in code paths that can execute in the background. Instead store those settings in your own preferences file, one whose data protection you can explicitly manage (in this case that means ‘set to NSFileProtectionNone’).
There are two problems with NSUserDefaults in a data protection context:
Its a fully abstract API: the presence and location of its backing store is not considered part of that API, so you can’t explicitly manage its data protection.
Note On recent versions of OS X NSUserDefaults is managed by a daemon and folks who try to manipulate its backing store directly have run into problems. It’s easy to imagine the same sort of thing coming to iOS at some point.
Even if changing the data protection were possible, NSUserDefaults has no mechanism to classify data based on the context in which you’re using it; it’s an ‘all or nothing’ API. In your case you don’t want to remove protection from all of your user defaults, just those that you need to access in the background before first unlock.
Finally, if any of this data is truly sensitive, you should put it in the keychain. Notably, the keychain does have the ability to set data protection on an item-by-item basis.
Source:
https://webcache.googleusercontent.com/search?q=cache:sR9eZNHpZtwJ:https://forums.developer.apple.com/thread/15685