I have an NSArray called namesArray.
I need to store all the names existing in namesArray using coredata.
How do i achieve this ?
Does we need to create any database like 'names.sqlite' using sqlite manager?
No you don't need to create a names.sqlite using manager. You should go through some of the tutorial found on the net for example: Here or Here.
You basically need to save in the datamodel in valueForKey format.
//Coredata saving
self.theAppDel=[[UIApplication sharedApplication] delegate];
NSManagedObjectContext*context=[self.theAppDel managedObjectContext];
NSManagedObject*object=[NSEntityDescription insertNewObjectForEntityForName:#"Contacts" inManagedObjectContext:context];
NSError*error=nil;
[object setValue:[namesArray objectAtIndex:0] forKey:#"name"]; //for saving first name
1. Creating an instance of your appdelegate .
2. Access your Managed Object Context and Managed Object
3. Assuming you created a entity description with name contacts. With the count of number of items in your array, add each object for a column named name (which i'm assuming you would have created).
This is short example, but you should go through the tutorials and read apple's documentation.
EDIT: As Ondra mentioned my earlier solution would have added only the last element. Use the following for adding: Adding NSMutableArray in CoreData Thanks Ondra Peterka
I think that iNoobs answer would not work - the loop would overwrite the value several times (only last name would be saved). In every case reading the tutorials is good idea.
Maybe the answer you are looking for is here: Saving an NSMutableArray to Core Data
Also ... I know you explicitly said "save to core data", but just in case - you can use also different storage like NSUserDefaults:
[[NSUserDefaults standardUserDefaults] setObject:namesArray forKey:#"namesArray"];
and retrieve it later using:
myArray= [[NSMutableArray alloc] initWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:#"namesArray"]];
Of course NSUserDefaults are ment to be used only for small amount of data.
Good luck ;)
Related
I have been working on an app where the user inputs data stored in core data everyday (two attributes an NSNumber and one as NSDate) and I wanted to improve that by allowing the user to import data from a external file such as csv or any other supported format through a button click. Any suggestions on how to proceed efficiently to do this?
Thank you.
Edit: Just adding a screenshot of the csv file as well as the output of the csv parser as NSArray. Basicly need to fetch the attribute separately and store them in core data on button click.
- The input file as csv:
- Sample csv parser output(NSarray):
I needed to achieve something similar recently.
A couple of members of my project team wanted to take our app prototype out to show potential clients, but wanted to show different data to each client. We solved this by allowing members of our project team to create their own test data before meeting with the client.
I achieved this by creating an example .csv file and distributing it to the other guys in the project team. They populate it with their own test data and use iTunes File Sharing to drop the .csv test data file on to the device.
On load, the app scans its Documents directory for a the test data file. If it exists, it parses the .csv file and persists to the database.
For the CSV parsing, I used Dave DeLong's CHCSVParser: https://github.com/davedelong/CHCSVParser
Plenty of help is available on setting up iTunes file sharing for your app. A quick Google finds this tutorial (http://www.raywenderlich.com/1948/how-integrate-itunes-file-sharing-with-your-ios-app) which should help you out, if you need it.
Edit- added help on storing data from .csv in Core Data
You stated in your original post that you store an NSNumber and NSDate. Taking that as a starting point, you might have a .csv file in the following form:
+----------------+--------------+
+ NSNumberColumn | NSDateColumn |
+----------------+--------------+
+ 1 | 2013-05-15 |
+ 2 | 2013-06-15 |
+ 3 | 2013-07-15 |
+----------------+--------------+
Assuming the output from the CSV parser is an NSArray of NSArrays, you could create the Core Data objects as follows:
I would create a couple of macros for the column numbers:
#define NSNumberColumn 0
#define NSDateColumn 1
Then iterate over the rows in the .csv file:
NSArray *rows = [NSArray arrayWithContentsOfCSVFile:pathToFile]; //CHCSVParser specific parsing method
for (NSArray *row in rows)
{
NSString *numberString = [parsedCsvRow objectAtIndex:NSNumberColumn];
NSString *dateString = [parsedCsvRow objectAtIndex:NSDateColumn];
NSNumber *number = // parse numberString to an NSNumber. Plenty of other posts on achieving this.
NSDate *date = // parse NSDate from dateString. Plenty of other posts on achieving this.
NSManagedObjectContext *context = [self managedObjectContext];
NSManagedObject *myCoreDataObject = [NSEntityDescription insertNewObjectForEntityForName:#"MyCoreDataObject" inManagedObjectContext:context];
[myCoreDataObject setValue:number forKey:#"NSNumberColumn"];
[myCoreDataObject setValue:date forKey:#"NSDateColumn"];
NSError *error;
if (![context save:&error]) {
NSLog(#"%#", [error localizedDescription]);
}
}
Note: Input validation and null checks have been ommited for brevity. I have also taken the liberty of making up your NSManagedObject property names, this will need updating. The above code should be separated in to a more suitable class structure.
I'm not at a Mac right now, so unfortunately I can't check if this works.
Hope that helps.
Try to use plist or json, they are already supported on iOS instead of CSV. CSV would require an third party parser. Using json or plist you will only need to loop throught the elemnts of the collections to create you persistent store. If you have just the CSV you can do a mid conversion using different free tools that you can find for free on the internet and later add to your bundle or publish to your site.
Here's what you do when you already have your CSV file parsed and data is ready to use in Objective-C.
Create a separate context for the import. You don't know how big the data can be, so you probably don't want to block one of your existing contexts while importing.
Iterate through the entries in the parsed data and insert new managed objects configured from each entry.
Every 200, 500, or 1000 entries (different for everybody, you'll need to test what's working best for you) save the context and, if needed, post a notification that a batch has been imported.
To keep the memory low, reset the context and forget all the objects that you created in this import context.
After the loop is finished, don't forget to save the last time.
Now how do you bring the data into another context, say, UI context?
This depends on the way you organized your Core Data stack. For example, import context can be configured as a child of the UI context. In this case, after each save to the import context the changes will be pushed to the UI context (and don't forget to save the UI context as well to push changes further).
But this is not the most efficient approach, because UI context, which is a context on the main thread, is involved in the import, and additional work is done on the UI thread that blocks it. I recommend creating the import context not as a child, but connected to the persistent store coordinator directly. To bring changes to the UI context in this case you either need to call mergeChangesFromContextDidSaveNotification: method after each save or you just refetch in the UI context after each save and in the end. The latter is easier on the UI context and particularly on NSFetchedResultsController, if you use it, because it doesn't need to replay changes to the updated objects one-by-one.
I'm developing an app that manages messages, and I want the app connects to the server, get messages and save them in the database(coredata). If the messages already exist, doesnt do anything and if they dont, add them to the database.
I'm thinking some ways to do it but I don't know exactly what to do. Any help? Thanks in advance
I would recommend using Restkit framework http://restkit.org
Reskit provides integration with Core Data.
Pros of using Restkit:
- Combines HTTP request/responses API, along with object mapping, offline/caching support with Core Data, all in one framework
- Object Mapping means that you're writing clean code, you define your classes and how they map to the JSON attributes, then you GET/POST/DELETE with few lines of code after that
- Core Data support means that your projects can work offline, data is sync when working online, but persistent when you need it offline
- The framework is well maintained
Cons:
- Works only with JSON REST APIs
- There can be a steep learning curve for some aspects
- Can be challenging if you work with REST APIs that are not completely 'standard'
The simplest way is to add a guid attribute (an identifier of type NSString, for example) to the entity you are interested in and check for that guid when you import data.
Here, you have two ways: let the server generate the guid for you or implement your own algorithm in the client side (iPhone, iPad, etc.). In both cases you need to be sure the guid is unique for each message.
So, for example, suppose the server generates the messages (and each message has its own guid). When you import data you also save the guid for each message object. If you have already a message with a specific guid, you don't add it, otherwise you add it. This could be done using the Find-or-Create pattern (see Implementing Find-or-Create Efficiently).
Hope that helps.
This is simple, it took me sometime to learn this, I use it in most of my apps.
First you need an ID of the fetched item, for example messageID.
When you fetch the JSON with all the items, for example using AFNetworking, you're going to receive an array of objects in NSDictionaries.
Before parsing the item load all the IDs of your stored items in a NSMutableDictionary (key => messageID, value objectID, this is related to the Core Data fault).
Don't forget to init the NSMutableArray somewhere:
_dictionaryOfEventIDAndObjectID = [NSMutableDictionary dictionary];
- (void)prepareDictionaryOfMessageIDs
{
[self.dictionaryOfEventIDAndObjectID removeAllObjects];
NSError *error = nil;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Message"];
[fetchRequest setResultType:NSDictionaryResultType];
NSExpressionDescription *objectIDDescription = [[NSExpressionDescription alloc] init];
objectIDDescription.name = #"objectID";
objectIDDescription.expression = [NSExpression expressionForEvaluatedObject];
objectIDDescription.expressionResultType = NSObjectIDAttributeType;
[fetchRequest setPropertiesToFetch:#[objectIDDescription, #"messageID"]];
NSArray *objectsDict = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
for (NSDictionary *objectDict in objectsDict) {
[self.dictionaryOfMessageIDAndObjectID setObject:[objectDict valueForKeyPath:#"objectID"] forKey:[objectDict valueForKeyPath:#"messageID"]];
}
}
Then in the fetched data completion block just add something like this:
for (NSDictionary *objectDict in objectsDict) {
NSString *fetchedID = [objectDict objectForKey:#"id"];
if ([self.dictionaryOfMessageIDAndObjectID objectForKey:fetchedID]) {
continue;
}
[self parseMessageFromDictionary:objectDict];
}
Currently, I have an application where a user clicks on a map and adds annotation points with certain subtitles. However, when the phone is power-cycled, all their added points are gone. I'm interested in making these annotations persistent. I've been trying to figure out how to do this with Core-Data, but after reading this tutorial here: http://www.raywenderlich.com/934/core-data-tutorial-getting-started, I'm a bit lost on where to start.
Any help would be appreciated, thanks.
If you have a core data application set up, you will only need to create an entity in the xcdatamodel file. Add attributes for whatever you may want to store.
latitude - double
longitude - double
title - NSString
etc. until you have what you want.
When you want to add an annotation, you should create a new core data object for your entity. It will look something like this
Location *newLocation = (Location *)[NSEntityDescription insertNewObjectForEntityForName:#"Location" inManagedObjectContext:self.managedObjectContext];
Location.latitude = ....
// and so on to store the information you want in its attributes;
You can set the attributes at different point if you change the values at a later point. You just need to be able to access the right object to go with the annotation. You should be able to do this by using NSFetchRequest in your managedObjectContext. You can use NSPredicate to filter the objects to the one you want. Then when you are ready to exit the app, save your context using
NSError *error = nil;
if ([managedObjectContext save:&error]) {
// handle the error;
}
which will store all the objects you've added to be used next time you open the app. You will then be able to create your annotations based on the objects in your managedObjectContext. I hope this is what you were looking for, or at least gives you an idea how to approach what you'd like to do.
What I'm trying is this:
1) Create a new manged object
2) Get it's temporary id with [myMO objectID];
3) Convert that ID to an NSURL, so I can save it for future reference:
NSManagedObjectID *moID = [myMO objectID];
NSURL *url = [moID URIRepresentation];
4) Save the managed object context
5) Some time later, fetch that object using the NSURL as ID
NSManagedObjectID *moID = [[context persistentStoreCoordinator] managedObjectIDForURIRepresentation:url];
And guess what: It does not work. I get an empty-stupid object back from
NSManagedObject *myOldMo = [context existingObjectWithID: moID error:&error];
But...as I said...the ID is temporary when creating an managed object. So it does make sense why this doesn't work at all. I must first save the context, and then I get a persistet ID. The real one. Right?
So is that the way to go?
1) Create the managed object
2) Save the context
3) Get the ID as NSURL
4) any time later, for example on your next birthday, access the managed object with the NSURL ;-)
I try to dream of NSManagedObjectID like a DB id which I can write on some yellow postIt sheet and glue on the middle of my monitor, so I refer back to it after lunch. You know... at least like in the old days where we used databases over telnet and executed SQL commands manually to query order information and stuff like that. The ID was the most important and significant thing, all the time.
But Core Data has this somewhat strange NSManagedObjectID thing.
What are your secret strategies? Do you actually recognize many use cases where you would need that NSManagedObjectID? Or is that something I could easily forget with no pain afterwards?
I'm not sure that it's such a big secret. The documentation describes the way to get permanent IDs for managed objects from the NSManagedObjectContext:
- (BOOL)obtainPermanentIDsForObjects:(NSArray *)objects error:(NSError **)error
http://developer.apple.com/iphone/library/documentation/Cocoa/Reference/CoreDataFramework/Classes/NSManagedObjectContext_Class/NSManagedObjectContext.html#//apple_ref/occ/instm/NSManagedObjectContext/obtainPermanentIDsForObjects:error:
I read: iPhone: How to Pass Data Between Several Viewcontrollers in a Tabbar App and was wondering what is the difference between
[[UIApplication sharedApplicaton] delegate]
and
extern struct* global
?
Conceptually, I don't see how [[UIApplication sharedApplicaton] delegate] not being a global thing. In fact, that lessen the sense of guilty when using the dirty global struct * now.
I am starting a new project very soon. So, I use this break to ask the question: is there any best-practice code example to illustrate how to share data between two ViewControllers (but not globally)?
Let me put it in an example:
this is a game
there is a NSString *name to store the player's name
there is a NSInteger score to store the player's current score
the GameMainViewController will update and display the score
in the GameSettingViewController, there is a text field to edit name and a button to reset score
the GameMainViewController is responsible for set a default name (if nil), save both name and score when exit, load both (if exists) when start
so
where should I put "name" and "score"?
how can both ViewControllers access and change the values
Thank you!
You can store name and score in NSUserDefaults.
Retrieve an item:
NSString *name = [[NSUserDefaults standardUserDefaults]objectForKey:#"name"];
Setting an item:
[[NSUserDefaults standardUserDefaults]setObject:#"Horace" forKey:#"name"];
Also, if this is data that you want to preserve across launches of the app, you may want to archive it into a plist.