Editor’s note: This is a sample chapter of our new book, Intermediate iOS 8 Programming with Swift.
When working with Core Data, you may have asked these two questions:
- How can you preload existing data into the SQLite database?
- How can you use an existing SQLite database in my Xcode project?
I recently met a friend who is now working on a dictionary app for a particular industry. He got the same questions. He knows how to save data into the database and retrieve them back from the Core Data store. The real question is: how could he preload the existing dictionary data into the database?
I believe some of you may have the same question. This is why I devote this tutorial to talk about data preloading in Core Data. I will answer the above questions and show you how to preload your app with existing data.
So how can you preload existing data into the built-in SQLite database of your app? In general you bundle a data file (in CSV or JSON format or whatever format you like). When the user launches the app for the very first time, it preloads the data from the data file and puts them into the database. At the time when the app is fully launched, it will be able to use the database, which has been pre-filled with data. The data file can be either bundled in the app or hosted on a cloud server. By storing the file in the cloud or other external sources, this would allow you to update the data easily, without rebuilding the app. I will walk you through both approaches by building a simple demo app.
Once you understand how data preloading works, I will show you how to use an existing SQLite database (again pre-filled with data) in your app.
Note that I assume you have a basic understanding of Core Data. You should know how to insert and retrieve data through Core Data. If you have no ideas about these operations, you can refer to our book, Beginning iOS 8 Programming with Swift or refer to this tutorial (in Objective-C).
A Simple Demo App
To keep your focus on learning data preloading, I have created the project template for you. Firstly, download the project and have a trial run.
It’s a very simple app showing a list of food. By default, the starter project comes with an empty database. When you compile and launch the app, your app will end up a blank table view. What we are going to do is to preload the database with existing data.

I have already built the data model and provided the implementation of the table view. You can look into the MenuItemTableViewController class and CoreDataDemo.xcdatamodeld for details. The data model is pretty simple. I have defined a MenuItem entity, which includes three attributes: name, detail, and price.
Once you’re able to preload the database with the food menu items, the app will display them accordingly, with the resulting user interface similar to the screenshot shown on the left.
The CSV File
In this demo I use a CSV file to store the existing data. CSV files are often used to store tabular data and can be easily created using text editor, Numbers or MS Excel. They are sometimes known as comma delimited files. Each record is one line and fields are separated with commas. In the project template, you should find the “menudata.csv” file. It contains all the food items for the demo app in CSV format. Here is a part of the file:
1 2 3 4 5 |
Eggs Benedict,"Poached eggs on toasted English muffin with Canadian bacon and Hollandaise sauce",11.0 Country Breakfast,"Two eggs as you like, Batter Home Fries, country slab bacon, sausage, scrapple or ham steak and toast",8.5 Big Batter Breakfast,"3 eggs, Batter Home Fries, toast, and 2 sides of meat (bacon, sausage, scrapple, or country ham)",13.5 Margherita Pizza,"Rustic style dough topped with tomato, basil, and fresh mozzarella",15.0 Fish and Chips,Battered cod and fresh cut French fries served with tartar or cocktail sauce,16.0 |
The first field represents the name of the food menu item. The next field is the detail of the food, while the last field is the price. Each food item is one line, separated with a new line separator.


Parsing CSV Files
It’s not required to use CSV files to store your data. JSON and XML are two common formats for data interchange and flat file storage. As compared to CSV format, they are more readable and suitable for storing structured data. Anyway, CSV has been around for a long time and is supported by most spreadsheet applications. At some point of time, you will have to deal with this type of file. So I pick it as an example. Let’s see how we can parse the data from CSV.
The AppDelegate object is normally used to perform tasks during application startup (and shutdown). To preload data during the app launch, we will add a few methods in the AppDelegate class. First, insert the following method for parsing the CSV file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
func parseCSV (contentsOfURL: NSURL, encoding: NSStringEncoding, error: NSErrorPointer) -> [(name:String, detail:String, price: String)]? { // Load the CSV file and parse it let delimiter = "," var items:[(name:String, detail:String, price: String)]? if let content = String(contentsOfURL: contentsOfURL, encoding: encoding, error: error) { items = [] let lines:[String] = content.componentsSeparatedByCharactersInSet(NSCharacterSet.newlineCharacterSet()) as [String] for line in lines { var values:[String] = [] if line != "" { // For a line with double quotes // we use NSScanner to perform the parsing if line.rangeOfString("\"") != nil { var textToScan:String = line var value:NSString? var textScanner:NSScanner = NSScanner(string: textToScan) while textScanner.string != "" { if (textScanner.string as NSString).substringToIndex(1) == "\"" { textScanner.scanLocation += 1 textScanner.scanUpToString("\"", intoString: &value) textScanner.scanLocation += 1 } else { textScanner.scanUpToString(delimiter, intoString: &value) } // Store the value into the values array values.append(value as! String) // Retrieve the unscanned remainder of the string if textScanner.scanLocation < count(textScanner.string) { textToScan = (textScanner.string as NSString).substringFromIndex(textScanner.scanLocation + 1) } else { textToScan = "" } textScanner = NSScanner(string: textToScan) } // For a line without double quotes, we can simply separate the string // by using the delimiter (e.g. comma) } else { values = line.componentsSeparatedByString(delimiter) } // Put the values into the tuple and add it to the items array let item = (name: values[0], detail: values[1], price: values[2]) items?.append(item) } } } return items } |
The method takes in three parameters: the file’s URL, encoding and an error pointer. It first loads the file content into memory, reads the lines into an array and then performs the parsing line by line. At the end of the method, it returns an array of food menu items in the form of tuples.
A simple CSV file only uses a comma to separate values. Parsing such kind of CSV files shouldn’t be difficult. You can call the componentsSeparatedByString method (highlighted in yellow in the code snippet) to split a comma-delimited string. It’ll then return you an array of strings that have been divided by the separator.
For some CSV files, they are more complicated. Field values containing reserved characters (e.g. comma) are surrounded by double quotes. Here is another example:
1 |
Country Breakfast,"Two eggs as you like, Batter Home Fries, country slab bacon, sausage, scrapple or ham steak and toast", 8.5 |
In this case, we cannot simply use the componentsSeparatedByString method to separate the field values. Instead, we use NSScanner to go through each character of the string and retrieve the field values. If the field value begins with a double quote, we scan through the string until we find the next double quote character by calling the scanUpToString method. The method is smart enough to extract the value surrounded by the double quotes. Once a field value is retrieved, we then repeat the same procedure for the remainder of the string.
After all the field values are retrieved, we save them into a tuple and then put it into the “items” array.
Preloading the Data and Saving it into database
Now that you’ve created the method for CSV parsing, we now move onto the implementation of data preloading. The preloading will work like this:
- First, we will remove all the existing data from the database. This operation is optional if you can ensure the database is empty.
- Next, we will call up the parseCSV method to parse menudata.csv.
- Once the parsing completes, we insert the food menu items into the database.
Add the following code snippets into the AppDelegate.swift file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
func preloadData () { // Retrieve data from the source file if let contentsOfURL = NSBundle.mainBundle().URLForResource("menudata", withExtension: "csv") { // Remove all the menu items before preloading removeData() var error:NSError? if let items = parseCSV(contentsOfURL, encoding: NSUTF8StringEncoding, error: &error) { // Preload the menu items if let managedObjectContext = self.managedObjectContext { for item in items { let menuItem = NSEntityDescription.insertNewObjectForEntityForName("MenuItem", inManagedObjectContext: managedObjectContext) as! MenuItem menuItem.name = item.name menuItem.detail = item.detail menuItem.price = (item.price as NSString).doubleValue if managedObjectContext.save(&error) != true { println("insert error: \(error!.localizedDescription)") } } } } } } func removeData () { // Remove the existing items if let managedObjectContext = self.managedObjectContext { let fetchRequest = NSFetchRequest(entityName: "MenuItem") var e: NSError? let menuItems = managedObjectContext.executeFetchRequest(fetchRequest, error: &e) as! [MenuItem] if e != nil { println("Failed to retrieve record: \(e!.localizedDescription)") } else { for menuItem in menuItems { managedObjectContext.deleteObject(menuItem) } } } } |
The removeData method is used to remove any existing menu items from the database. I want to ensure the database is empty before populating the data extracted from the menudata.csv file. The implementation of the method is very straightforward if you have a basic understanding of Core Data. We first execute a query to retrieve all the menu items from the database and call the deleteObject method to delete the item one by one.
Okay, now let’s talk about the preloadData method.
In the method we first retrieve the file URL of the menudata.csv file using this line of code:
1 |
NSBundle.mainBundle().URLForResource("menudata", withExtension: "csv") |
After calling the removeData method, we execute the parseCSV method to parse the menudata.csv file. With the returned items, we insert them one by one by calling the NSEntityDescription.insertNewObjectForEntityForName method.
Lastly, execute the preloadData() method in the didFinishLaunchingWithOptions method:
1 2 3 4 5 6 |
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { preloadData() return true } |
Now you’re ready to test your app. Hit the Run button to launch the app. If you’ve followed the implementation correctly, the app should be preloaded with the food items.
But there is an issue with the current implementation. Every time you launch the app, it preloads the data from the CSV file. Apparently, you only want to perform the preloading once. Change the application:didFinishLaunchingWithOptions: method to the following:
1 2 3 4 5 6 7 8 9 10 11 |
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { let defaults = NSUserDefaults.standardUserDefaults() let isPreloaded = defaults.boolForKey("isPreloaded") if !isPreloaded { preloadData() defaults.setBool(true, forKey: "isPreloaded") } return true } |
To indicate that the app has preloaded the data, we save a setting to the defaults system using a specific key (i.e. isPreloaded). Every time when the app is launched, we will first check if the value of the “isPreloaded” key. If it’s set to true, we will skip the data preloading operation.
Using External Data Files
So far the CSV file is bundled in the app. If your data is static, it is completely fine. But what if you’re going to change the data frequently? In this case, whenever there is a new update for the data file, you will have to rebuild the app and redeploy it to the app store.
There is a better way to handle this.
Instead of embedding the data file in the app, you put it in an external source. For example, you can store it on a cloud server. Every time when a user opens the app, it goes up to the server and download the data file. Then the app parses the file and loads the data into the database as usual.
I have uploaded the sample data file to the Amazon cloud server. You can access it through the URL below:
1 |
https://s3.amazonaws.com/swiftbook/menudata.csv |
This is just for demo purpose. If you have your own server, feel free to upload the file to the server and use your own URL. To load the data file from the remote server, all you need to do is make a little tweak to the code. First, update the preloadData method to the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
func preloadData () { // Remove all the menu items before preloading removeData() var error:NSError? let remoteURL = NSURL(string: "https://s3.amazonaws.com/swiftbook/menudata.csv")! if let items = parseCSV(remoteURL, encoding: NSUTF8StringEncoding, error: &error) { // Preload the menu items if let managedObjectContext = self.managedObjectContext { for item in items { let menuItem = NSEntityDescription.insertNewObjectForEntityForName("MenuItem", inManagedObjectContext: managedObjectContext) as! MenuItem menuItem.name = item.name menuItem.detail = item.detail menuItem.price = (item.price as NSString).doubleValue if managedObjectContext.save(&error) != true { println("insert error: \(error!.localizedDescription)") } } } } } |
The code is very similar to the original one. Instead loading the data file from the bundle, we specify the remote URL and pass it to the parseCSV method. That’s it. The parseCSV method will handle the file download and perform the data parsing accordingly.
Before running the app, you have to update the application:didFinishLaunchingWithOptions: method so that the app will load the data every time it runs:
1 2 3 4 5 6 |
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { preloadData() return true } |
You’re ready to go. Hit the Run button and test the app again. The menu items should be different from those shown previously.
For your reference, you can download the complete Xcode project here.
Using An Existing Database in Your Project
Now that you should know how to populate a database with external data, you may wonder if you can use an existing SQLite database directly. In some situations, you probably do not want to preload the data during app launch. For example, you need to preload hundreds of thousands of records. This will take some time to load the data and results a poor user experience.
Apparently, you want to pre-filled the database beforehand and bundle it directly in the app.
Suppose you’ve already pre-filled an existing database with data, how can you bundle it in your app?
Before I show you the procedures, please download the starter project again. As a demo, we will copy the existing database created in the previous section to this starter project.
Now open up the Xcode project that you have worked on earlier. If you’ve followed me along, your database should be pre-filled with data. We will now copy it to the starter project that you have just downloaded.
But where is the SQLite database?
The database is not bundled in the Xcode project but automatically created when you run the app in the simulator. To locate the database, you will need to add a line of code to reveal the file path. Update the application:didFinishLaunchingWithOptions: method to the following:
1 2 3 4 5 6 7 |
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { println(applicationDocumentsDirectory.path) preloadData() return true } |
The SQLite database is generated under the application’s document directory. To find the file path, we simply print out the value of applicationDocumentsDirectory.path variable.
Now run the app again. You should see an output in the console window showing the full path of the document directory.

Copy the file path and go to Finder. In the menu select Go > Go to Folder… and then paste the path in the pop-up. Click “Go” to confirm.

Once you open the document folder in Finder, you will find three files: CoreDataDemo.sqlite, CoreDataDemo.sqlite-wal and CoreDataDemo.sqlite-shm.

Starting from iOS 7, the default journaling mode for Core Data SQLite stores is set to Write-Ahead Logging (WAL). With the WAL mode, Core Data keeps the main .sqlite file untouched and appends transactions to a .sqlite-wal file in the same folder. When running WAL mode, SQLite will also create a shared memory file with .sqlite-shm extension. In order to backup the database or use it to in other projects, you will need copy these three files. If you just copy the CoreDataDemo.sqlite file, you will probably end up with an empty database.
Now, drag these three files to the starter project in Xcode.

When prompted, please ensure the “Copy item if needed” option is checked and the “CoreDataPreloadDemo” option of “Add to Targets” is selected. Then click “Finish” to confirm.


Now that you’ve bundled an existing database in your Xcode project. When you build the app, this database will be embedded in the app. But you will have to tweak the code a bit before the app is able to use the database.
By default, the app will create an empty SQLite store if there is no database found in the document directory. So all you need to do is copy the database files bundled in the app to that directory. In the AppDelegate class update the declaration of the persistentStoreCoordinator variable like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator? = { // The persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail. // Create the coordinator and store var coordinator: NSPersistentStoreCoordinator? = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel) let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("CoreDataDemo.sqlite") if !NSFileManager.defaultManager().fileExistsAtPath(url.path!) { let sourceSqliteURLs = [NSBundle.mainBundle().URLForResource("CoreDataDemo", withExtension: "sqlite")!, NSBundle.mainBundle().URLForResource("CoreDataDemo", withExtension: "sqlite-wal")!, NSBundle.mainBundle().URLForResource("CoreDataDemo", withExtension: "sqlite-shm")!] let destSqliteURLs = [self.applicationDocumentsDirectory.URLByAppendingPathComponent("CoreDataDemo.sqlite"), self.applicationDocumentsDirectory.URLByAppendingPathComponent("CoreDataDemo.sqlite-wal"), self.applicationDocumentsDirectory.URLByAppendingPathComponent("CoreDataDemo.sqlite-shm")] var error:NSError? = nil for var index = 0; index < sourceSqliteURLs.count; index++ { NSFileManager.defaultManager().copyItemAtURL(sourceSqliteURLs[index], toURL: destSqliteURLs[index], error: &error) } } var error: NSError? = nil var failureReason = "There was an error creating or loading the application's saved data." if coordinator!.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: nil, error: &error) == nil { coordinator = nil // Report any error we got. var dict = [String: AnyObject]() dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data" dict[NSLocalizedFailureReasonErrorKey] = failureReason dict[NSUnderlyingErrorKey] = error error = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict) // Replace this with code to handle the error appropriately. // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. NSLog("Unresolved error \(error), \(error!.userInfo)") abort() } return coordinator }() |
The changes are highlighted in yellow. We first verify if the database exists in the document folder. If not, we copy the SQLite files from the bundle folder to the document folder by calling the copyItemAtURL method of NSFileManager.
That’s it! Before you hit the Run button to test the app, you better delete the CoreDataPreloadDemo app from the simulator or simply reset it (select iOS Simulator > Reset Content and Settings). This is to remove any existing SQLite databases from the simulator.
Okay, now you’re good to go. When the app is launched, it should be able to use the database bundled in the Xcode project.
For reference, you can download the final Xcode project here.
Editor’s note: This is a sample chapter of our new book, Intermediate iOS 8 Programming with Swift. If you like this tutorial, you can check out the book here or get the starter package of our Swift book package.
Comments
culture2010
AuthorThank you for the clear and nice tutorial. This is just what i needed.
Simon Ng
AuthorGlad to hear that! Thanks.
lakshmi
AuthorHello sir, can you please provide the same example in Objective-C.
Dave
AuthorGreat tut – thanks 🙂 Does this download the database every time, or can the database be stored on the app until there’s an update to the database? For example – if the database updates only 3 times a year, it seems a little excessive to download it each time the app launches.
Ismael Idrissi
Authorif your database exists , it will not download it anymore.
however , don’t you think we have a double database stored in the project …. don’t we need to erase the dabatase once it has been imported , making it become as a normal core data ???
Axel Gora
AuthorHi everyone,
thanks for the tuto it’s awesome.
I just have a problem with this line
If textScanner.scanLocation < count (textScanner.string) {
I have a problem with textScanner.string the compiler say 'String' is not convertible to 'Range ‘
if you can help me it would be great
thanks
Sumesh Agarwal
Authoruse textScanner.scanLocation < textScanner.string.characters.count instead
King Allem
AuthorHi There is a way to get this tutorial in Objective-C ?
Simon Ng
AuthorKing, sorry that we do not have the Objective-C version of the tutorial. With the release of Swift 2, we will put all our focus on Swift.
Filipe
AuthorAw.. Seriously thats you’ll doesn’t provide this tutorial in Objective-C?!! Please!
HongKongTom
AuthorHi King,
I found that maybe it helps you.
http://notatkiprogramisty.blox.pl/2011/12/Importing-CSV-data-file-into-an-iOS-app-via-email.html
Chris
AuthorHello. I see you mentioned Swift 2, Simon.
I’m running Swift 2 under Xcode 7 Beta 2. As you know, try-catch is different.
Will these code snippets be re-written in October, or could we expect this beforehand?
jper
Authorfor var index = 0; index < sourceSqliteURLs.count; index++ {
try! NSFileManager.defaultManager().copyItemAtURL(sourceSqliteURLs[index], toURL: destSqliteURLs[index])
}
/*
This works just as well as try! but try! keyword communicates your intent clearly: you're aware there's the theoretical possibility of the call failing, but you're certain it won't happen in your use case. For example, if you're trying to load the contents of a file in your app's bundle, any failure effectively means your app bundle is damaged or unavailable, so you should terminate. (Thx, Hacking with Swift .com see Error handing)
do {
try NSFileManager.defaultManager().copyItemAtURL(sourceSqliteURLs[index], toURL: destSqliteURLs[index])
} catch _ {
}
}
*/
}
Ethan Humphrey
AuthorIs there a way I could do this with iCloud implemented?
Sumesh Agarwal
Authorin func parseCSV instead of
let content = String(contentsOfURL: contentsOfURL, encoding: encoding, error: error It should be
if let data = NSData(contentsOfURL: contentsOfURL) {
if let content = NSString(data: data, encoding: NSUTF8StringEncoding) {
}
}
Philippe Badizé
AuthorFirst, thanks a lot for your tutorial, Simon ng ! It helps a lot !
Regarding your comment, Sumesh :
if let content = String(contentsOfURL: contentsOfURL, encoding: encoding, error: error)
is compliant with this parseCSV prototype :
func parseCSV(contentsOfURL: NSURL, encoding: UInt, error: NSErrorPointer) -> [(name: String, detail: String, price: String)]?{…}
I only changed this and it works fine.
Harry M
AuthorHello Simon,
a rather late reply to to the post, but I was curious about Apple’s core data guidelines and your second example. I understand the technical solution of loading an existing database to a project, but from what I gather this will be rejected by Apple, since it doesn’t allow (from what I understand) pre-loaded applications in the Documents folder, but suggests to have it in the cache folder or mark it as ‘not-backup’.
Could you maybe elaborate on what steps we’d need to do to avoid getting our app rejected by adding the existing database into our projects?
Manolo Suarez
AuthorHi Harry are you sure apple would reject the app? Did you get any reply from your post?
Georgios Traskas
AuthorHello Harry,
I agree with your concerns loading the pre-loaded database inside the documents folder. Have you found any solution?
Khushwant Singh
AuthorI have followed the exact same technique and my app got approved in the first shot. So if coded properly it wont be rejected by Apple.
Juan Diego Delgado Vargas
AuthorHello Simon!
I actually have a question here, I’ve read in different threads that you shouldn’t be calling API and heave/processing stuff in the didFinishLaunchingWithOptions method because if there is a long wait, It will just kill your app. I love the tutorial, and Im actually doing something similar, but not sure if you are aware of my concern here.
Regards,
Juan
Ralf Daniel
AuthorHi Simon,
great tutorial. I am playing around with it for a few days and got it to work yesterday with xCode 6.4.
Today i upgraded to xCode 7.0 and tried to get it to work. Most errors could be handled but I am stuccoing around wit following error message:
initializer for conditional binding must have optional type not ‘nsmanagedobjectcontext’
This is coming up in the fun preloadData and removeData in the line with
if let managedObjectContext = self.managedObjectContext {
Another error I do not get fixed is the new error handling in swift 2 with the error
extra argument ‘error’ in call
in line
menuItems = managedObjectContext.executeFetchRequest(fetchRequest, error: &e) as! [MenuDgItem]
Somebody any idea or is there an update for xCode 7 available .
Thanks!
Fred Fredson
AuthorYep. I’m stuck here as well. Teh intarwebnets have revealed nada so far.
Alex Sikand
AuthorCheck out my comment above
Alex Sikand
AuthorRemove the “if” from the “if let” statement to fix the first error you described and also make sure to remove the brackets that enclosed that if statement. For the second error you need to delete the second parameter from the function call.
Just call managedObjectContext.executeFetchRequest(fetchRequest)
Error handling has changed in Swift 2 so instead of passing in this error as a parameter you use do, try, catch, and throws to handle errors. I recommend searching “do try catch throw swift 2” on Google or something like that and you will have your code up and running in no time.
rkj2
AuthorHi
I was thinking of purchasing the Intermediate iOS8 Book in Swift. However, I held off when I found code in this tutorial breaking in many ways in Swift 2.0 working in XCode 7.0
Is there any plan to relook at example code to refactor/correct it for the book releases ?
Thanks. I definitely like the scope of the articles and the lucid manner in which things are explained.
HongKongTom
AuthorHi Simon,
Thanks for that tutorial it s the only working (in iOS8) tut i found on the net. I would ask the same like rkj2. But my question is how would you approach if the cvs list does have a title row. e.g. Name, Description, Price. or if the list is different like this time only name, description, price and next time it name, description, ingreadians, price and then next time maybe information about allergy compatibility. what i try to ask is if the list grows. (i know that I also have to change the core data base, but lets say i ve created enough attributes in entity)
Khushwant Singh
AuthorHi and thanks for this wonderful tutorial. I have followed your tutorial and shipped pre-loaded Sqlite along with my app. It works like charm and my app got approved. I now need to perform changes in my Sqlite DB by adding new entities. Can you please also write a blog showing how to upgrade sqlite db?
Thanks in advance
Vince
AuthorHi Simon,
Awesome tutorial. I’m working with the Parse Backend framework and need to be able to parse JSON files to CoreData. Would love to know if you have a tutorial like this that shows how to do the CSV parsing with JSON instead.
Cheers
Subway Korea
AuthorHelpful tutorial! but here is an issue with the last method “using an existing database in your project”.
What if some data are changed and reuploaded to app store?
How would I update ~.sqlite, ~.sqlite-wal, ~.sqlite-shm files with new versions if “!NSFileManager.defaultManager().fileExistsAtPath(url.path!)” returns false all the time?
Bryan
AuthorThis is great but how do I do anything with the data from the table. Right now it’s just a static table view, I want to load another view with a full description or something. I’ve seen didSelectRowAtIndexPath but I’m not sure how to implement it.
Nicholas Fantuzzi
AuthorI needed to work a little bit on the code since Swift 2 and Xcode 7 has been recently introduced. But this tutorial is awesome! Clear, simple and easy to follow. Thank you very much!
Janbask – Online IT Training
AuthorVery helpful tutorial.
Sean Zhang
Authordo {
let menuItems = try managedObjectContext.executeFetchRequest(fetchRequest) as! [MenuItem]
print (menuItems)
} catch {
print(“Failed to retrieve record: (error)”)
}
}
for menuItem in menuItems {
managedObjectContext.deleteObject(menuItem)
}
This is giving me an error about Expected Declaration, do you know what is the problem?
Sean Zhang
Authordo {
let menuItems = try managedObjectContext.executeFetchRequest(fetchRequest) as! [MenuItem]
print (menuItems)
} catch {
print(“Failed to retrieve record: (error)”)
}
}
for menuItem in menuItems {
managedObjectContext.deleteObject(menuItem)
}
This is giving me an error about Expected Declaration, do you know what is the problem?
Cor Prunus
AuthorHi Simon,
Parsing n gives as result \n
I an using this cvs parser as you wrote it. In my cvs file there are strings with literals as n or r for linefeed or carriage return. After parsing the result is that the code n has been changed to \n and r to \r. So with a double backslash.
In a label this is interpreted as text and not as a linefeed.
What is the solution for this?
Thanks for your answer.
Cor
Masoud r
AuthorHI Simon plz help
i bought your book.
when i open your zip file and type all code in Appdelegate.swift every thing work fine,
but when i create new project and copy and paste same appdelegate code to new project.
in new project i made new entity as you did make nsobject class for it copy menudata.csv to project.
it give me an error in the line
let modelURL = NSBundle.mainBundle().URLForResource(“CoreDataDemo”, withExtension: “momd”)!
Thread :EXC_BAD_INSTRUCTION(code = EXC_i386_INVOP_subcode = 0x0)
did you modify something in actual project properties?
Ujjwal Nadhani
AuthorI would really appreciate an update to Swift 3. Great Content 🙂
Yerbol
AuthorCould you please update your code to the latest Swift 3? Best regards!
Marcos Borges
Author//
// ExecCargaCSV.swift – swift 4.0 xcode 9
// Created by Marcos Borges on 24/11/2017.
//
import UIKit
import CoreData
class ExecCargaCSV: UIResponder, NSFetchedResultsControllerDelegate {
// MARK: – CSV Parser Methods
func parseCSV (contentsOfURL: NSURL, encoding: String.Encoding) -> [(brinco:String, dataproducao:String, quantidademanha:String, quantidadetarde:String)]? {
// Load the CSV file and parse it
let delimiter = “;”
var items:[(brinco:String, dataproducao:String, quantidademanha:String, quantidadetarde:String)]?
do {
let content = try NSString(contentsOf: contentsOfURL as URL, encoding: encoding.rawValue)
// print(content)
items = []
let lines:[String] = content.components(separatedBy: NSCharacterSet.newlines) as [String]
for line in lines {
var values:[String] = []
if line != “” {
// For a line with double quotes
// we use NSScanner to perform the parsing
if line.range(of: “””) != nil {
var textToScan:String = line
var value:NSString?
var textScanner:Scanner = Scanner(string: textToScan)
while textScanner.string != “” {
if (textScanner.string as NSString).substring(to: 1) == “”” {
textScanner.scanLocation += 1
textScanner.scanUpTo(“””, into: &value)
textScanner.scanLocation += 1
} else {
textScanner.scanUpTo(delimiter, into: &value)
}
// Store the value into the values array
values.append(value! as String)
// Retrieve the unscanned remainder of the string
if textScanner.scanLocation < textScanner.string.count {
textToScan = (textScanner.string as NSString).substring(from: textScanner.scanLocation + 1)
} else {
textToScan = ""
}
textScanner = Scanner(string: textToScan)
}
// For a line without double quotes, we can simply separate the string
// by using the delimiter (e.g. comma)
} else {
values = line.components(separatedBy: delimiter)
}
// Put the values into the tuple and add it to the items array
let item = (brinco: values[0], dataproducao: values[1], quantidademanha: values[2], quantidadetarde: values[3])
items?.append(item)
}
}
} catch {
print(error)
}
return items
}
func cargaproducaoCSV () {
var fetchResultProducaoController: NSFetchedResultsController!
var producao: ProducaoMO!
var producoes:[ProducaoMO] = []
var fetchResultAnimalController: NSFetchedResultsController!
var animal: [AnimalMO] = []
var animalexiste = true
// Load the data file. For any reasons it can’t be loaded, we just return
guard let contentsOfURL = NSURL(string: “https://docs.google.com/spreadsheets/d/e/2PACX-1vSIlWyfiYPQmQwactrJBsiyX2mt5YBTuFrFFipa6T6UpM2PSIPGSUnOdv_KdttHxvqQBW7S0hpcH7HQ/pub?gid=715266805&single=true&output=csv”) else {
return
}
if let items = parseCSV(contentsOfURL: contentsOfURL, encoding: String.Encoding.utf8) {
//Grava CSV no Banco Local
for item in items {
// verificar se já existe lançamento
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = “dd/MM/yyyy”
let datatemp = dateFormatter.date(from:item.dataproducao)
// verifica se animal cadastrado
let fetch1Request: NSFetchRequest = AnimalMO.fetchRequest()
let sortDescriptor = NSSortDescriptor(key: “brinco”, ascending: true)
fetch1Request.sortDescriptors = [sortDescriptor]
fetch1Request.predicate = NSPredicate(format:”brinco == %@”,item.brinco)
if let appDelegate = (UIApplication.shared.delegate as? AppDelegate) {
let context = appDelegate.persistentContainer.viewContext
fetchResultAnimalController = NSFetchedResultsController(fetchRequest: fetch1Request, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
fetchResultAnimalController.delegate = self
do {
try fetchResultAnimalController.performFetch()
if let fetchedObjects = fetchResultAnimalController.fetchedObjects {
animal = fetchedObjects
if animal.count > 0 {
animalexiste = true
} else {
animalexiste = false
}
}
}
catch {
print(“achou um erro (error)”)
}
}
//
if animalexiste == true {
let fetchRequest: NSFetchRequest = ProducaoMO.fetchRequest()
let sortDescriptor = NSSortDescriptor(key: “dataproducao”, ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
fetchRequest.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [NSPredicate(format:”dataproducao == %@”,datatemp! as CVarArg), NSPredicate(format:”brinco == %@”,item.brinco)])
if let appDelegate = (UIApplication.shared.delegate as? AppDelegate) {
let context = appDelegate.persistentContainer.viewContext
fetchResultProducaoController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
fetchResultProducaoController.delegate = self
do {
try fetchResultProducaoController.performFetch()
if let fetchedObjects = fetchResultProducaoController.fetchedObjects {
producoes = fetchedObjects
if producoes.count > 0 {
producao = producoes[0]
producao.quantidademanha = (item.quantidademanha as NSString).doubleValue
producao.quantidadetarde = (item.quantidadetarde as NSString).doubleValue
producao.quantidadedia = producao.quantidadedia + producao.quantidadetarde
appDelegate.saveContext()
} else {
if let appDelegate = (UIApplication.shared.delegate as? AppDelegate) {
producao = ProducaoMO(context: appDelegate.persistentContainer.viewContext)
producao.brinco = item.brinco
producao.dataproducao = datatemp
let anoref = Calendar.current.component(.year, from: datatemp!)
let mesref = Calendar.current.component(.month, from: datatemp!)
producao.anoproducao = Int64(anoref)
producao.mesproducao = Int64(mesref)
producao.nrlactacao = Double(animal[0].numerocrias)
producao.quantidademanha = (item.quantidademanha as NSString).doubleValue
producao.quantidadetarde = (item.quantidadetarde as NSString).doubleValue
producao.quantidadedia = producao.quantidademanha + producao.quantidadetarde
appDelegate.saveContext()
}
}
}
}
catch {
print(“achou um erro (error)”)
}
}
}
}
}
}
}
Hishou
AuthorI third the Swift 3 request!
Ikai Hung
AuthorI changed a little bit, This is my Swift 3 code:
lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator? = {
// The persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
// Create the coordinator and store
var coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
let url = self.applicationDocumentsDirectory.appendingPathComponent(“CoreDB.sqlite”)
if !FileManager.default.fileExists(atPath: url!.path) {
let sourceSqliteURLs = [Bundle.main.url(forResource: “CoreDB”, withExtension: “sqlite”)!, Bundle.main.url(forResource: “CoreDB”, withExtension: “sqlite-wal”)!, Bundle.main.url(forResource: “CoreDB”, withExtension: “sqlite-shm”)!]
let destSqliteURLs = [self.applicationSupportDirectory.appendingPathComponent(“CoreDB.sqlite”),
self.applicationDocumentsDirectory.appendingPathComponent(“CoreDB.sqlite-wal”),
self.applicationDocumentsDirectory.appendingPathComponent(“CoreDB.sqlite-shm”)]
var error:NSError? = nil
for index in 0…sourceSqliteURLs.count-1 {
do {
try FileManager.default.copyItem(at: sourceSqliteURLs[index], to: destSqliteURLs[index]!)
} catch let error as NSError {
print(“Error: (error.domain)”)
}
}
}
do {
try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: nil)
} catch let error as NSError {
print(“Ops there was an error (error.localizedDescription)”)
abort()
}
return coordinator
}()
Alexandre Araujo Barbosa
AuthorThis app does not display the data!
Tigran
AuthorHI Everyone.I have question ,and here it is. Which is the best practice for using existing data ,SQLite or other things,and can i update that data in future.Thanks.
Reham Hobus
AuthorI read your article about preloaded SQLite. And applied long time ago and it work great. But now I am using Xcode 10.1 for ios12 with swift 4 but Apple changed everything in the AppDelegate and I am not able to figure out how to do it. Every time I change something or add it gives error or won’t work.
Is it possible to update your article for the new Apple new changes.
Thank you
Jan
AuthorHi,
Thank you for this tutorial. Would you like to update it to xcode 11 please?
Thank you