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
Related
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.
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.
Can anyone give a description of each of the 5 setup methods?
(void) setupCoreDataStack;
(void) setupAutoMigratingDefaultCoreDataStack;
(void) setupCoreDataStackWithInMemoryStore;
(void) setupCoreDataStackWithStoreNamed:(NSString *)storeName;
(void) setupCoreDataStackWithAutoMigratingSqliteStoreNamed:(NSString *)storeName;
What do they each do and what is the use case for each?
setupCoreDataStack
Use this when you're just getting started with MagicalRecord. This will, as the method states, set up your default Core Data stack. The pieces of the stack are well know and comprise of: NSPersistentStore, NSPersistentStoreCoordinate, NSManagedObjectModel and a default NSManagedObjectContext. At least one of each of these must be instantiated and configured properly for Core Data to work. MagicalRecord provides this single method to configure your stack with a SQLite persistent store located in /Library/Application Support//.sqlite
setupAutoMigratingDefaultCoreDataStack
When you version your model, you will need to migrate your data. This method will do the same as the previous (above) method, but will also enable Auto Migrations.
setupCoreDataStackWithInMemoryStore;
Sometimes, such as when you're writing unit tests, you want your data to go away when your app terminates. This method will also set up a CoreData stack (as mentioned above) but instead of a SQLite store, it creates a persistent store in system memory (RAM).
setupCoreDataStackWithStoreNamed:(NSString *)storeName
Sometimes you want to customize the file name where your data resides. This method does the same as the first, namely setting up the core data stack, and placing a SQLite store in the specific location, but instead of .sqlite, the store is named storeName.sqlite
setupCoreDataStackWithAutoMigratingSqliteStoreNamed:(NSString *)storeName
This does the same as the above method, but also enabled auto migrations. You'll need to do this when you version your model and have a simple migration that simply needs to be enabled.
You can read more on Core Data migrations on Apple's Core Data Reference Documentation
For example. if I have a chat app that uses objects like: ChatRoom and ChatMessage. (both subclasses of NSManagedObject)
And throughout the app I need to: search chat rooms / add massages / create chat rooms and messages / and any other manipulate.
Is it ok if I do everything directly with core data? I mean every time I need to search a chat room or something like that, to do it with NSFetchRequest or NSFetchedResultsController.
You can access the data whenever you need, of course. On the other hand you should try to use caching mechanism as much as possible.
For example, if you are using your data in UITableView you should defintely go with NSFetchedResultsController as it was created explicitly for UITableViews. From the apple docs on NSFetchedResultsController:
It optionally monitors changes to objects in its associated managed object context, and reports changes in the results set to its delegate (see “The Controller’s Delegate”).
It optionally caches the results of its computation so that if the same data is subsequently re-displayed, the work does not have to be repeated (see “The Cache”).
Otherwise, if you need the data just temporarily, you can access them of course using NSFetchRequest each time they are needed or cache them in your business objects if they don't change or you know their lifetime otherwise.
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)