Storing objects in iOS for later use - objective-c

My app is pulling in JSON data from our web service. In all instances up til now, the information would be retained in memory or just refreshed on the fly with no need to retain anything locally other than the login token used in the API. With one of the new features we are adding, we will be taking in a group of locations, 26 max, with long, lat, radius, and name.
I also need to add 1-2 fields to this data to create a larger object. So my question is, what would be the best way to store this type of data in the iOS filesystem? Currently I have been using the NSUserDefaults, but that seems sort of limited or ill advised for larger amounts of data. Maybe not.
This data will need to be retrieved, changed or edited, and resaved. All of this while still retaining the ability to pull any of those 26 objects. Thank you in advance for reading and helping out.

For such a small amount of data (26 items) I suggest archiving.
Save to plist using NSKeyedArchiver/NSKeyedUnarchiver. Read your data from the delegate's didFinishLaunchingWithOptions, and listen for UIApplicationWillResignActiveNotification to save it.
A NSUserDefaults is a plist with features designed to store user preferences. It's often used instead a regular plist to save a couple of lines of code, which I think it's a bad idea because you get an additional complexity unrelated to your task.
If you want the login to be protected against someone stealing the device and performing forensics use the Keychain. You may want to use a wrapper and read some articles, comment if you are interested.
If you look for more features see Best way to store data on iphone but doesn't seem to be the case now.
Some code to get you started... Register to call save on app resign:
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(saveMyData)
name:UIApplicationWillResignActiveNotification
object:nil];
On each object of the graph/dictionary/whatever you want to archive implement NSCoding:
- (void)encodeWithCoder:(NSCoder*)coder {
[coder encodeObject:myIvar forKey:kmyIvar];
}
- (id)initWithCoder:(NSCoder*)coder {
if((self = [super initWithCoder:coder])) {
self.myIvar = [[coder decodeObjectForKey:kmyIvar] retain];
}
return self;
}

Check out this guide on core data. It's the best way to store data locally on your device. It's a native cocoa API and it is bindings compatible. Plus, you can choose whether to store data as XML, SQLite, and Binary.
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreData/cdProgrammingGuide.html
For anything remotely large, I would use this.

I had this same question and just figured out a much better solution.
What you could do is just store the JSON string as your NSUserDefault. Then, when you reload the app use the same method (or framework utility) you used to map the JSON string to your objects the first time. This way you can still take advantage of the ease of NSUserDefaults.
If you're using RestKit to manage your web services it gets even easier. The answer on this post shows how to use RestKit's JSON parser to map from JSON to your object.
Deserializing local NSString of JSON into objects via RestKit (no network download)

Related

implementing multi-process shared DB in macOS using XPC

My Goal is to develop robust, coherent, and persistent DB that can be shared between processes, just list Windows Registry.
On a previous question I advised not to use CFPreferences (and NSUserDefaults) due to the following reason
Current versions of macOS have a great deal of difficulty, and sometimes outright refuse, to update the values in one process with the values set by a second process.
Alternatively, I advised to use the following approach:
To have one process responsible for all of the shared values and the other processes get/set those values via XPC.
The XPC is quite new to me but from what I've read so far, it seems to use GCD queues for each connection, so how can I keep coherency if there are multiple processes asking to access the same DB for R/W operations (how can I enforce single thread to execute items in all queues).
Furthermore, I'd like to make this DB to fulfill ACID requirements, how can I achieve this ?
Here's my suggestion, and the solution I use in my applications.
(1) Create a named XPC service.
If you need to connect with your service from multiple apps, you'll need to name and register your service app with launchd.
(XPC make it pretty easy to create an anonymous service used only by your app, but connecting from other apps gets a little trickier. Start with the Daemons and Services Programming Guide.)
Note that in my solution, I already had a user agent registered with launchd, so this was just a matter of moving on to step (2).
(2) Add XPC message handlers to get and set the values you want to share.
- (void)queryPreferenceForKey:(NSString*)key withReply:(void(^)(id value))reply
{
reply([[NSUserDefaults standardUserDefaults] objectForKey:key]);
}
- (void)setPreferenceValue:(id)value forKey:(NSString*)key withReply:(void(^)(BOOL changed))reply
{
BOOL changed = NO;
id previous = [[DDUserPreferences standardUserDefaults] objectForKey:key];
if (!OBJECTS_EQUAL(previous,value))
{
[[NSUserDefaults standardUserDefaults] setObject:value forKey:key];
changed = YES;
}
reply(changed);
}
(3) There is no step 3.
Basically, that's it. The NSUserDefault class is thread safe, so there are no concurrently issues, and it automatically takes care of serializing the property values and synchronizing them with the app's persistent defaults .plist file.
Note: since this is based on NSUserDefaults, the value objects must be property-list objects (NSString, NSNumber, NSArray, NSDictionary, NSDate, NSData, ...). See Preferences and Settings Programming Guide.

Steps to take in migrating data from Core Data to Realm?

I was reading this article by Tim Oliver (Migrating an App from Core Data to Realm) and I came across the following paragraph, which has been the most guidance I have been able to find as far as converting my app from Core Data to Realm:
"Once you’ve migrated to Realm, you can re-link the Core Data framework back into your app, use raw NSManagedObject objects to fetch your users’ data from Core Data, and then manually pass it over to Realm. You can leave this migration code in your app permanently, or simply remove it after a sufficient period of time has passed."
This seems like a good idea; however, I have not been able to find even one example of how this should be done.
I have already changed all of my Core Data classes over to Realm, so they cannot be used any more. I have the xcdatamodel file available to the app and the core data framework is easily turned back on. Do I need to set up the whole persistent store controller, managedObjectContext, etc. all over again?
While undesirable, that can be done if necessary. Then I am left to figure out, given that 'raw' NSManagedObject will need to be used, how the many-to-many and one-to-many relationships and their inverses will be able to be properly captured.
Can anyone point me in the right direction on this? I am using objective-c but would benefit from examples in Swift if there are any to be pointed to.
I'm the Tim Oliver who wrote that article. :)
So in my own app (for which that article was based on), when I moved from Core Data to Realm, I did a clean break. All of the data I was storing was just cached metadata derived by the document files in the app, so I opted to simply delete the Core Data SQLite file on disk, and then it was only going to be a mild inconvenience the next time the user opened the app as the metadata was re-calculated and stored in Realm.
If you've got data in your Core Data file, then the onerous is on you to perform a one-off migration from Core Data to Realm. It's not recommended to access the raw data from the SQLite file directly, so the only way is to keep your Core Data code around and use it to query and copy the data.
What I meant by 'raw NSManagedObjects' is that since Core Data objects are KVC-compliant, if you've already refactored your model classes into Realm classes, you can access the data straight from the base NSManagedObject class using the KVC methods like -[NSObject valueForKey:].
I briefly tested this in one of the official Apple Core Data sample code projects to confirm it was still working.
Originally, accessing data from a Core Data Book object looked like this:
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
// Configure the cell to show the book's title
Book *book = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = book.title;
}
But once you've refactored your Book class into RLMObject, you can still access data from your Core Data store like this:
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
// Configure the cell to show the book's title
NSManagedObject *book = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = [book valueForKey:#"title"];
}
In this way, you should still be able to access the data in your Core Data objects like before, but you're free to use the actual classes for Realm now.
Unfortunately, like you mentioned in the comments, this does mean you need to keep some amount of Core Data code around in order to be able to open the Core Data store and get the data out of it.
But ideally, you should be able to confine all of this code to a single 'migrator' class that only needs to run the first time it detects that the user's data hasn't been copied to Realm yet. And after a sufficient amount of time has passed, you could then consider dropping it completely.

Use RESTKit for two way synchronization

is it possible to use RESTKit for two way synchronization?
I played aroud with RESTKit and CoreDate. Now I can download all data from my REST service and all changes (create/modify/delete objects) in CoreDate will be overwritten by RESTKit.
Now I want to choose between both versions (the local version or the remote version). How do I do this? Is there a way to manipulat the mapping, or something like that?
update:
My app should synchronize after some changes or after a specific delay (not difficult). Every object has a change date. Until now I want to keep the newer one (if they are equal the local one).
I hope RestKit is made for changing the strategy how it merges objects. something like a block I can set, where I get both objects and can return the merged object.
What I got so far:
I load the object via RestKit but do not persist them. Also I setup a CoreData store where I store the local objects. After loading the remote object I start to synchronize my self. First searching for pairs and then decide which to take, delete, create, overwrite, and so on...
But this is a big bunch of work and I think RestKit is doing something similar. Why not simply changing the strategy of RestKit for my requirements.
Well this would be the "syncing down" thing. After this I could write the synchronized data set back to the service. The changes are not very frequently so I will not have to check for new changes.
I hope now it's a little bit clearer
What you really want to do is validate the data coming in.
Since RestKit is using CoreData it automatically uses the validation built into CoreData (see here)
Here is an example that will ensure that the date never gets changed to an earlier one.
- (BOOL) validateChangeDate:(id *)ioValue error: (NSError **)outError {
if ([[*ioValue laterDate:self.changeDate] compare:self.changeDate] == NSOrderedSame)
*ioValue = self.changeDate;
return YES;
}
Note: There may be better/faster ways to test to see if we need to change the date
Note 2: According to the docs, if RestKit sees a value rejected, it fails the entire object, which we don't want, so that's why we always return YES and just change the incoming date
Note 3: We also don't want to change the value unless we have to because of what the CoreData docs say here
You may be able to leverage KVC validation to achieve this as it allows you to check and then edit or deny the change for each object and key. Check these docs.

Optimal data store memory usage?

I'm in the process of building a data store for keeping track of all the remote images being stored in my app. I've decided to use a singleton that will keep track of all the images being referenced.
As I remember, iOS automatically begins purging objects from memory based on recency of usage, whether or not it's being referenced by the current view controller, etc. However, if I store these images in a data store, those objects are always being referenced by the store itself. My solution for memory management was to keep track of when images were last called and keep some form of limit on the # and size of images being stored in the data store and purge the oldest based on age.
Is this solution a good one? Why or why not? Should I be depending on Apple's automatic memory management, or is having my own manager fine?
Further explanation:
Here's how requesting an image from one of my view controllers will end up looking with my solution:
[[HollerImages store]getImageWithUrl:#"https://www.google.com/logos/classicplus.png"
completionBlock:^(BOOL succeeded, UIImage *image){
if( succeeded ){
//Update the UIImageView with the returned image
}
}];
The store will then manage how many images are currently being referenced in the app and automatically de-reference old images as we hit some pre-defined limit. Thoughts?
Renaud Boisjoly (#rboisjoly) just sent me a link to this library which appears to provide the solution I was describing: https://github.com/rs/SDWebImage/
The easiest way to handle memory concerns is to just implement the -(void)didReceiveMemoryWarning function and clear out all your cached data there.
What you're talking about is implementing an expiring cache. You could just count the elements in your data structure at each insertion and remove elements from the head when you've hit the limit (provided it is an ordered data structure). The former solution is easier and works in most cases.

iOS 5 Maintain variables and state in an application?

I am new to iOS 5 programming so I'm sure that these are basic questions for the experienced folks.
I have a log in form that creates a unique session string. How can I maintain string in a way that it will be usable on all view controllers throughout the application just for that session?
How can I store a series of strings (maybe 1 or 2 of them) so that they will be available to the application on subsequent application loads? In other words, how can I maintain a default string that can be used throughout the lifetime of the application on any given device?
First, this can be stored on the Application Delegate (which is accessible like below from anywhere within your application:
YourAppDelegate.h
- (NSString *)uniqueSessionString;
View Controller:
NSString *uniqueString = [(YourAppDelegate *)[[UIApplication sharedApplication] delegate] uniqueSessionString];
Second, to save this information look at NSUserDefaults. This information will persist even after the application closes. Here is a tutorial on using it here:
http://mobile.tutsplus.com/tutorials/iphone/nsuserdefaults_iphone-sdk/
If you need to maintain this string for all of a user's devices, then you need to look at the NSUbiquitousKeyValueStore (part of iCloud). You also can use both of these methods together. See this SO question:
How to use NSUbiquitousKeyValueStore and NSUserDefaults together