I have an app that has some data ( static ) that needs to be shipped out with the app. The client gave me this data in Excel(over 300 rows), now I created a model for this in coredata, I was wondering is there a way or a tutorial that shows how can I make an import from excel into my model ? or if someone thinks of a better approach to incorporate this data( SQLLite maybe)?
Thanks
There is a way to import CSV data into sqlite3 at the command line; however, it is recommended that you not do this, since Core Data creates sqlite fields that would need to be dealt with.
If you start with CSV, you could look at doing something like this:
http://macresearch.org/cocoa-scientists-part-xxvi-parsing-csv-data
perhaps with modification to work with your dataset.
To import the data into your application and store it in Core Data I would export the Excel Data to a CSV file. That filetype is easy to parse. I don't know about any libraries that parse Excel files directly. Your code does than look like this.
NSString *fileContent = [NSString stringWithContentsOfFile:#"path/to/file.csv" usedEncoding:&enc error:nil];
NSArray *lines = [fileContent componentsSeparatedByString:#"\n"];
for (NSString *currentLine in lines) {
// Handle this line and store in model
NSArray *fields = [currentLine componentsSeperatedByString:#";"]; // Or other delimiter
// From the excel file you know which column is which property in the model.
// Populate the entity appropriately and store it
}
The above code is without guarantee. I didn't test the code.
Related
I have the following requirement: create and populate a SQLite database with data from a .xml file, this file can have a different structure any time so I cannot create the NSManagedObjectModel with Xcode, it must be at runtime. I've successfully created a NSManagedObjectModel programmatically, created the persistent store and populated the sqlite file with data from the .xml . However, the next time I use the app the persistent store is not compatible with the NSManagedObjectModel (I don't want to recreate the Model every time I run the app, just the first time). Is there any way to save the model I created programmatically and load it the next time it is needed? All I can see in the examples are models being loaded from the NSBundle.
Is there any way to save the model I created programmatically and load it the next time it is needed?
Yes. NSManagedObjectModel conforms to NSCoding, which means that you can easily convert it to/from NSData, and saving and reading NSData is easy.
To save a model:
NSString *modelPath = // path where you want to save
NSData *modelData = [NSKeyedArchiver archivedDataWithRootObject:self.managedObjectModel];
[modelData writeToFile:modelPath atomically:YES];
To read a saved model:
if ([[NSFileManager defaultManager] fileExistsAtPath:modelPath]) {
NSData *savedModelData = [NSData dataWithContentsOfFile:modelPath];
NSManagedObjectModel *savedModel = [NSKeyedUnarchiver unarchiveObjectWithData:savedModelData];
}
I'm not sure if you are saying that the data in the xml file is changing each time or what. It sounds like you are referring to the data, not the data model. I can't answer specifically, but I would take the approach as follows.
If the data in the xml file is structured the same or close to the same each time, I would create a data model to match that.
Then I would write some sort of parser class that would read the xml and parse it into the Core Data data store according to you "ManagedObjectModel" or data model.
I have seen the error you are talking about when you change the datastore outside of Core Data. You need to let Core Data handle all the reading and writing to the data store or else Core Data will tell you basically that "Your Persistent Store was created or altered by something other than your ManagedObjectModel". I think this is what is happening.
I know I am not using the terminology exactly as Core Data puts it, but Core Data is confusing and I'm trying to convey the message and understanding.
I would also look in to using MagicalRecord. It Drastically makes Core Data easier to work with and there is a great tutorial on www.raywenderlich.com which you can find Here
I really hope this helps you out some. If not, please post some sample code or maybe an example of that xml you are referring to.
Good Luck
Purpose: I have to create entities from files.
So entities represent my data model in CoreData and files have all information for this entities.
All files I get from Internet. For this I use AFNetworking framework.
How I get files (algorithm):
Request plist file. Plist file has values to other urls that I have to download.
When plist was downloaded to my Documents directory on device. I parse it.
When I parse plist I grab url from each item from NSDictionary that represent this plist.
Then I request zip files from this urls.
After zip files were downloaded I unzip them and go to the next step.
Parse unzipped files and create data model.
It is my problem. I have version of file that stored locally and that stored on the server and when version on the server changed I need to reload my data model with actual data. The bad way it is load all data from server again next delete all entities in storage and make new entities from new data. But it is not professional way at first and second it is an additional burden on the traffic, because if I have just one entity that I need to reload why I have to reload other entities that are in the actual state. So maybe someone knows best practice with this question. Of course I can create my solution and it will work, but I want to see how people solve this problem and figure out with the differences in my solution and in the other solutions also.
This is trivial. You simply keep an attribute with the time stamp of the last update and just request the changed and new entities from your server. Then you insert, update or delete as appropriate.
It sounds like you are talking about find-or-create. Depending on the size of the data set and your performance requirements you can do this a couple of ways:
The first way is to fetch your existing Core Data objects and store them in an a dictionary with a unique attribute of the entity serving as the key. Then when you download the new data you can take this key for each parsed object and check your dictionary to find (and optionally update) any existing object, or create a new one:
NSArray *fetchedObjects = //Fetch the existing objects;
NSMutableDictionary *existingObjects = [NSMutableDictionary dictionary];
for (MyClass *object in fetchedObjects)
[existingObjects setObject:object forKey:object.uniqueKey];
//Now iterate through the new data (using JSON as example here)
NSDictionary *downloadedItems = //Download data and serialise into JSON
for (NSDictionary *item in downloadedItems){
NSString *uniqueValue = [item valueForKey:#"uniqueKey"];
MyClass *object = [existingObjects objectForKey:uniqueValue];
if (object==nil){
object = //Insert new MyClass entity into MOC
object.uniqueKey = uniqueValue;
//Set any other values you only need to set once
}
//Set any values you may need to update every time
//This would be where to check a modified date attribute
}
The second way is more sophisticated, and involves less memory overhead. It's described in the Efficiently Importing Data section of the Core Data Programming Guide.
The guide gives a good start but doesn't offer a complete solution; I attempted my own in an answer here: Basic array comparison algorithm
I'm new to iOS but have plenty of experience with c++ and Python. I'm trying to figure out how to read a plaintext file I have on my computer into an NSArray in xcode. In c++ I would do this:
while(istr>>string) myArray.push_back(string);
However, I need to create a local copy to be stored on the iOS device. Is there a way I can package this data so that a local copy of JUST THE ARRAY will be stored on the device? I was thinking of maybe doing something with a JSON serialization or something.
Should I really just suck it up and do this:
NSArray myArray = [[NSArray alloc] initWithObjects: #"myInfo", nil];
I just want a more elegant way to handle this, I guess.
I think maybe you're thinking a little too C about this. In C and C++, strings are arrays of bytes. In ObjC, there's an object for that. It's called NSString, and it's probably what you should be storing plaintext in.
It even has an easy class method to help you out with this if you already have a byte array:
+(id)stringWithCString:(const char *)cString encoding:(NSStringEncoding)enc
See the NSString documentation for more details.
As to storing it on the device, there are solutions that range from the simple (NSUserDefaults) to the complex (Core Data), but pretty much anything will expect plain text be in an NSString.
EDIT:
The title of this question talks about reading the string from the filesystem. First step is to get the bytes of the file into an NSData object. The easy way:
+(id)dataWithContentsOfFile:(NSString *)path
Then make a string out of the data with this initializer of NSString:
-(id)initWithData:(NSData *)data encoding:(NSStringEncoding)encoding
I don't know if this can help you, anyway if you just need to store an array of data to filesystem and deserialize it back to NSArray, an easy way is to use plists.
It is a convenient way to store a small amount of data, without any kind of relationship (there is Core Data for that). The main advantage is that you can store in it NSArray, NSDictionary, NSNumber, NSString, NSDate and NSData (so any kind of binary information) and they get automatically serialized and deserialized through some simple methods.
You can write an NSArray to a file in this way:
- (BOOL)writeToFile:(NSString *)path atomically:(BOOL)flag
and deserialize it back with this:
+ (id)arrayWithContentsOfFile:(NSString *)aPath
If you just want to provide some initial data to your app, and it is for example an array of strings or something similar, you can manually add a plist to your project by going to File->New->File and choosing Resources->Property list, and fill it by hand.
You can read more at https://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/PropertyLists/Introduction/Introduction.html
I'm working on an application that will need to pull from a list of data depending on where the user is located in the US. In a sense, I will have a database full of information based on their location, and a condition statement will determine while value from the list to use.
Example of data:
Tennessee:
Data2 = 25;
Data3 = 58;
...
Texas:
Data2 = 849;
Data3 = 9292;
...
So on...
My question is, what is the best practice to use when developing iOS apps and you have a lot of data? Should you just put all the related data in a file, and import that file when you need to like normal, or is there another method you should use? I know they state you should follow the MVC practice, and I think in this case my data would be considered the Model, but just want to double check if that applies here.
You have some options here:
SQLite database
Core Data (its not a relational database model like sqlite)
write to plain text file. (using NSFileManager )
NSKeyedArchiever
If you want to frequently keep appending data to a single file, I would recommend using sqlite fast and robust.
I'm creating a simple iOS application consisting of a few UITableViewControllers. The information displayed in the view controllers will come from a text file (that I'll include in the project's Resources). The text file's contents will come from a spreadsheet.
Since this is my first time working with Core Data I have a few questions:
What format is most common for the text file? CSV, XML or something else?
What's the easiest way to import the data?
A few notes:
The data is static. Ideally the app will load the data into "Core Data" just once (1st time the app is run).
Each additional run of the app will just pull data from some Core Data source (that I'm not completely familiar w/ yet) instead of re-loading it from the textfile.
If the data is structured in a relational way then XML or JSON allows that structure to be easily preserved and then easily parsed and saved in your Core Data store. You'll need to use an XML or JSON parser, which will turn your data into an array of dictionaries (or multiple levels thereof if your data structure requires it). You'll simply iterate through the array and dig into the dictionaries (and sub-arrays and sub-dictionaries, if appropriate) and add objects to your store as you go.
If it's flat data, a simple single table that will become a single entity in Core Data, then tab-delimited or CSV text files are fine (and tab-delimited is even easier to parse if there wouldn't be any tabs within the data itself). You can then grab individual rows, break the rows down into an array of data bits (this is where tab delimiting makes is super-simple), create a new object for each row, set its properties to the array elements, and save the context.
The XML/JSON version is more complex than is worth writing out here -- search SO and you'll find lots of examples -- but here's the tab-delimited version (this assumes you don't have a gigantic ball of data that can't reasonably be held in memory):
// Standard Core Data setup here, grabbing the managedObjectContext,
// which is what I'll call it
// Then parse your text
NSString *path = [[NSBundle mainBundle] pathForResource:#"YourTextFileName" ofType:#"txt"];
NSString *content = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL];
NSArray *rows = [content componentsSeparatedByString:#"\n"];
// Now that we have rows we can start creating objects
YourManagedObject *yourManagedObject = nil;
for (NSString *row in rows) {
NSArray *elements = [row componentsSeparatedByString:#"\t"];
YourManagedObject *yourManagedObject = (YourManagedObject *)[NSEntityDescription insertNewObjectForEntityForName:#"YourManagedObject" inManagedObjectContext:managedObjectContext;
[YourManagedObject setName:[elements objectAtIndex:0]];
[YourManagedObject setCountry:[elements objectAtIndex:1]];
// Etc. You may need an NSNumberFormatter and/or an NSDateFormatter to turn
// your strings into dates and numbers, depending on your data types
[managedObjectContext save];
}
Poof, all done.
If the data doesn't change, why bother including the text file in the app? Instead, create Core Data file on your Mac and include that as a resource in the app. I presume it's a lot of data that'll take a while to parse, so there's no sense in making your users each wait for that to happen when you could do the parsing once and distribute the result.
To make that happen, take the data model and the parsing code from your app and use them to build a small command-line app that just reads the text file, writes the Core Data file, and exits.