iOS - truly persistent data? - ios5

I'm exploring a business model that specifies how many times a (costly) function may be invoked per user in an application.
Right now whatever I save, e.g. user defaults, gets deleted when the app is deleted. When the app is reinstalled, the counter is back to zero.
What I need is a way to save information such that deleting the app still leaves the information intact.
I've seen examples that mimic the deprecated unique identifier, but that's per device, which is not what I'm looking for.

KeyChains appear to be the right way to go. I can store a unique ID there specific to the user as well as in the user defaults and that way can track also if the app was reinstalled.

Related

Merge NSManagedObject with equal properties, but different IDs, synced with iCloud results in duplicates (Core Data, Swift 1.2)

I have a Swift 1.2 app that uses Core Data with iCloud sync.
In the first screen, the user can insert some data to create custom MyNSManagedObjects.
Every MyNSManagedObject must have a specific "group" to which it belongs. This "category" is represented in my data model by another custom NSManagedObject, let's call it MyManagedObjectsCategory.
User can create many MyManagedObjectsCategory objects, but the app also needs a DEFAULT object of type MyManagedObjectsCategory, in case the user doesn't create any different MyManagedObjectsCategory.
Every MyNSManagedObjects can have only 1 MyManagedObjectsCategory, but a MyManagedObjectsCategory can have many MyNSManagedObjects.
When user launches the app, I immediately check if the DEFAULT MyManagedObjectsCategory already exists and if it doesn't I create a DEFAULT MyManagedObjectsCategory object and save it to persistent store. Of course, only the first time the app launches I need to create this object, later I always fetch the object I created on the first launch of the app and use that.
My issue started when I enabled iCloud sync; now, on the first launch of the app, this happens:
The app on launch uses STORAGE 1 (local) as expected and, not finding the DEFAULT object of type MyManagedObjectsCategory, it creates a new one.
If there's network coverage, a few seconds later the app switches to STORAGE 0 (cloud) and saves the DEFAULT MyManagedObjectsCategory object that has just been created; if the device is offline, this doesn't happen immediately, but of course it will occur when network connection becomes available later.
When I launch the app for the first time on a different device, the point 1 and 2 above happen again: since the app starts with storage 1, it doesn't fetch the DEFAULT MyManagedObjectsCategory object and it creates and saves a new one that, a few seconds later or when network is available, is synced to storage 0 as the app switches storage.
As you can imagine, when different devices sync I find myself with multiple DEFAULT objects and, since I'm new to Core Data, I have no idea how to manage this issue.
On one hand, I need the DEFAULT object immediately available when the app launches, so I can't wait the switch to storage 0 (also, because I don't know if the user has network connection, so the storage switch might happen much later); on the other hand, the purpose of the DEFAULT object is to be one, and always the same, on every device.
I understand that, even if every DEFAULT object has matching properties (the object has a name and a myID String property) being created in the exact same way, Core Data creates a unique ID for every managed object and, since the ID doesn't match between the DEFAULT objects created on different devices in different moments, it doesn't merge them in a single DEFAULT object.
So, my questions:
Is there a way to force this merge of the DEFAULT objects into a single one, if certain properties are exactly the same? Is so, how? I suppose I could do it when the app launches, since the duplication of the DEFAULT object would only happen when a new device is added to iCloud.
Is there a completely different way to handle this issue that I'm missing?
I've spent the last 2 months working on this app, but I can't ship something that duplicates objects when syncing, and I have no idea how to fix it, so any help would really, really be appreciated.
Thanks,
#cdf1982
This is fundamental to using iCloud, or really any sync mechanism. If your app creates the same instance on multiple devices, and can't sit around waiting to see if it already exists from a different device, then you'll get duplicates.
The only way to handle this is to let the duplicates happen and then clean them up. With iCloud, you do the cleanup when you receive NSPersistentStoreDidImportUbiquitousContentChangesNotification, indicating that new incoming data is available. The basic scheme is to do a fetch that finds all duplicates and then handles them according to your app's needs (merging/deleting/whatever). I described this in a previous answer and in some detail in a blog post.
You'll make this much, much easier on yourself if your category entity has an attribute that stores a unique ID, and you ensure that you always use the same unique ID value for your default category instance. Then you can simplify the de-duplication by fetching only objects that match the known unique ID value.

Password login for ios app

I am currently developing an app for a company that is in a very competitive field. I have finished all of the features of the app that they requested except for one, making it somehow protected from their competing companies to download and use. I thought that I could set up a UIViewController with a password field that would check against some kind of database, but I'm not sure how to do the checking against a database part nor the practicality of it, and was hoping I could get some ideas on how to do this so that other companies couldn't steal and use this app without a password or something that changes like every 30 days or something and is kind of like an activation code.
Review the WWDC 2012 video "Building and Distributing Custom B2B Apps for iOS". I'm unsure if your app is in this B2B classification, it seems that it might be from your description.
What I ended up doing (if everyone needs a reference) was setting up a server with an SQL table that has pass codes in it. Since apple does not allow for any sort of system that requires you to "buy the app from outside the app store" I made a dumby username field (shame on me) that takes any value you like and then requires to have a pass code that fits. Once the pass code gets authenticated with the web server in a json sql request (there are plenty of api's to do this with) it comes back and sends the user to the first screen and sets a value in a plist with how many days of use the user has left. Whenever the user opens up the app it checks to see if the date is different from the last date logged in (saved in the same plist file) and if it is different then it calculates the difference and deducts that many. When the count reaches 0 it sends the user to the pass code authentication screen again. A bit complicated but an effective method of getting around Apple's restriction on not having a sort of pass code system like this. Thanks for the answers, unfortunately enterprise did not work for this company since they needed to be able to distribute the app to as many 3rd party members as they wanted to without having to worry about them leaving the company for other suppliers and remote management of the app (I.e ability to remote uninstall) was also not an option. Hope this helps someone someday!

Whatever happens to iOS Application's data persistence when the App gets deleted?

I want to keep some of the critical(and less memory consuming) data of my App(ex: licence key or user credentials), stored in the device. In normal scenario, when the user deletes the App from the device, all the data related to the particular App gets deleted.
But I would like to override this behavior, and store some important stuff inside the device's disk, so that it does not get deleted along with the App. How to do that? Any help is much appreciated.
you can always store the license key and user credentials in the ios keychain.. it will stay there even if you delete the app. You can use the SSKeyChain API to access the key chain. Keep in mind you can only trivial string info this way.
Hopefully for obvious reasons that isn't possible. (What if every app did that? They you'd delete every app on your device and still find that gigabytes of space were being taken up by "deleted" apps.)
No, if you want data to be saved even after deletion, you'd need to store it in "the cloud" somewhere.

iOS Development: How can I make changes to my app so that it only affects new users?

I'd like to make some drastic changes to my iOS app, but I don't want those changes to affect existing installs, since it may cause existing users to complain. How can I limit the new functionality to new installs only?
One easy way would be to set a global flag at a point in the code that only occurs before the user completes the tutorial, then if that flag is set, use the new code. Other, more elegant solutions?
Thanks so much for your wisdom!
I'll set aside whether you should do this. Other answerers have said what I have to say about that.
Technically how you'd do this is by looking for any data that persists when a user updates the app. So, any NSUserDefaults values you've set, anything you've written to the app's sandbox, keychain values... Any hint you've been here before, and you set yourself a userDefaults flag to only show old stuff. Otherwise, set yourself a flag to only show new stuff.
If you want 2 different app behaviors, you can just submit a new different app to the App store. Apple may or may not like "hidden" behaviors that they can't find during review.
the most elegant solution that I can think of is letting the user choose.
if your changes are so well delimited that you can use a flag (like you say in your question) to decide which code you run, I would add a setting useNewUI which is by default YES unless you detect the app was already installed (read further for more details about this). Then the user can go to the settings (you might also ask them) and decide and at any moment change.
You could decide whether your user is a new or an old one by checking whatever data you are storing in NSUserDefaults. At the start of the program, you check, if data is there, then you add useNewUI to NSUserDefault with a value of NO, otherwise you add it with value YES.
This approach could based on NSUserDefault could be useful to you even if you disregard the possibility of letting the user choose through a setting.
If the app is radically different, then make a new app. Otherwise, anyone who updates will get the new one.
Some existing users may want to have the new one, some may want the old one. This gives everyone the choice.
You can't. Once you submit an update, all of your users can download it and will have the new feature. If the update is going to annoy your existing customers, it will probably annoy your new users too.

How to save data in NSUserDefaults even if app will be deleted?

I save some strings and numbers to NSUSerDefault,
but when I uninstall and reinstall the app the data is ereased.
Is there a possibility to store data some where else? Maybe in keychain?
There are two places where you can store data that will persist after an app is deleted, but in both cases, it's not hidden data, so if that's what you're looking for, you're out of luck.
Your two possibilities are 1) Saving data to the photo library. (However, you can't read it back unless you get the user to select the photo for you.) 2) The address book. This is one place where you can create an entry and select it without user input.
You could stash a small amount of data in the comments field of a keychain record.
However, I doubt Apple would allow it because it would violate the principle of sandboxing the apps. It's hard to think of a legitimate reason to have an app leave data behind after it's gone.