Do you know of any way to delete all of the entries stored in Core Data? My schema should stay the same; I just want to reset it to blank.
Edit
I'm looking to do this programmatically so that a user can essentially hit a reset button.
You can still delete the file programmatically, using the NSFileManager:removeItemAtPath:: method.
NSPersistentStore *store = ...;
NSError *error;
NSURL *storeURL = store.URL;
NSPersistentStoreCoordinator *storeCoordinator = ...;
[storeCoordinator removePersistentStore:store error:&error];
[[NSFileManager defaultManager] removeItemAtPath:storeURL.path error:&error];
Then, just add the persistent store back to ensure it is recreated properly.
The programmatic way for iterating through each entity is both slower and prone to error. The use for doing it that way is if you want to delete some entities and not others. However you still need to make sure you retain referential integrity or you won't be able to persist your changes.
Just removing the store and recreating it is both fast and safe, and can certainly be done programatically at runtime.
Update for iOS5+
With the introduction of external binary storage (allowsExternalBinaryDataStorage or Store in External Record File) in iOS 5 and OS X 10.7, simply deleting files pointed by storeURLs is not enough. You'll leave the external record files behind. Since the naming scheme of these external record files is not public, I don't have a universal solution yet. – an0 May 8 '12 at 23:00
You can delete the SQLite file - but I choose to do it by purging the tables individually with a functions:
- (void) deleteAllObjects: (NSString *) entityDescription {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:entityDescription inManagedObjectContext:_managedObjectContext];
[fetchRequest setEntity:entity];
NSError *error;
NSArray *items = [_managedObjectContext executeFetchRequest:fetchRequest error:&error];
[fetchRequest release];
for (NSManagedObject *managedObject in items) {
[_managedObjectContext deleteObject:managedObject];
DLog(#"%# object deleted",entityDescription);
}
if (![_managedObjectContext save:&error]) {
DLog(#"Error deleting %# - error:%#",entityDescription,error);
}
}
The reason I chose to do it table by table is that it makes me confirm as I am doing the programming that deleting the contents of the table is sensible and there is not data that I would rather keep.
Doing it this will is much slower than just deleting the file and I will change to a file delete if I this method takes too long.
Updated Solution for iOS 10+
Use NSBatchDeleteRequest to delete all objects in the entity without having to load them into memory or iterate through them.
// create the delete request for the specified entity
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = MyEntity.fetchRequest()
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
// get reference to the persistent container
let persistentContainer = (UIApplication.shared.delegate as! AppDelegate).persistentContainer
// perform the delete
do {
try persistentContainer.viewContext.execute(deleteRequest)
} catch {
print(error.localizedDescription)
}
This code has been updated for iOS 10 and Swift 3. If you need to support iOS 9, see this question.
Sources:
Core Data: Quickest way to delete all instances of an entity (includes Objective-C code)
What's New in Core Data (WWDC 2015 video)
What's New in Core Data (WWDC 2016 video)
How to Use Core Data in iOS 10
What’s new in Core Data Swift 3.0
I've written a clearStores method that goes through every store and delete it both from the coordinator and the filesystem (error handling left aside):
NSArray *stores = [persistentStoreCoordinator persistentStores];
for(NSPersistentStore *store in stores) {
[persistentStoreCoordinator removePersistentStore:store error:nil];
[[NSFileManager defaultManager] removeItemAtPath:store.URL.path error:nil];
}
[persistentStoreCoordinator release], persistentStoreCoordinator = nil;
This method is inside a coreDataHelper class that takes care of (among other things) creating the persistentStore when it's nil.
I remove all data from core data on a button Event in a HomeViewController class:
This article helped me so much I figured I'd contribute.
-(IBAction)buttonReset:(id)sender
{
NSLog(#"buttonReset Pressed");
//Erase the persistent store from coordinator and also file manager.
NSPersistentStore *store = [self.persistentStoreCoordinator.persistentStores lastObject];
NSError *error = nil;
NSURL *storeURL = store.URL;
[self.persistentStoreCoordinator removePersistentStore:store error:&error];
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:&error];
NSLog(#"Data Reset");
//Make new persistent store for future saves (Taken From Above Answer)
if (![self.persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
// do something with the error
}
}
Note that in order to call self.persistentStoreCoordinator I declared a property in the Home View Controller. (Don't worry about the managedObjectContext that I use for saving and loading.)
#property (nonatomic, retain) NSManagedObjectContext * managedObjectContext;
#property (nonatomic, retain) NSPersistentStoreCoordinator * persistentStoreCoordinator;
Then in the AppDelegate ApplicationDidFinishLaunching right below creating a HomeViewController I have :
homeViewController = [[HomeViewController alloc] initWithNibName:#"HomeViewController" bundle:nil];
homeViewController.managedObjectContext = self.managedObjectContext;
homeViewController.persistentStoreCoordinator = self.persistentStoreCoordinator;
iOS9+, Swift 2
Delete all objects in all entities
func clearCoreDataStore() {
let entities = managedObjectModel.entities
for entity in entities {
let fetchRequest = NSFetchRequest(entityName: entity.name!)
let deleteReqest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
do {
try context.executeRequest(deleteReqest)
} catch {
print(error)
}
}
}
MagicalRecord makes this very easy.
[MyCoreDataObject MR_truncateAll];
[Late answer in response to a bounty asking for newer responses]
Looking over earlier answers,
Fetching and deleting all items, as suggested by #Grouchal and others, is still an effective and useful solution. If you have very large data stores then it might be slow, but it still works very well.
Simply removing the data store is, as you and #groundhog note, no longer effective. It's obsolete even if you don't use external binary storage because iOS 7 uses WAL mode for SQLite journalling. With WAL mode there may be (potentially large) journal files sitting around for any Core Data persistent store.
But there's a different, similar approach to removing the persistent store that does work. The key is to put your persistent store file in its own sub-directory that doesn't contain anything else. Don't just stick it in the documents directory (or wherever), create a new sub-directory just for the persistent store. The contents of that directory will end up being the persistent store file, the journal files, and the external binary files. If you want to nuke the entire data store, delete that directory and they'll all disappear.
You'd do something like this when setting up your persistent store:
NSURL *storeDirectoryURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"persistent-store"];
if ([[NSFileManager defaultManager] createDirectoryAtURL:storeDirectoryURL
withIntermediateDirectories:NO
attributes:nil
error:nil]) {
NSURL *storeURL = [storeDirectoryURL URLByAppendingPathComponent:#"MyApp.sqlite"];
// continue with storeURL as usual...
}
Then when you wanted to remove the store,
[[NSFileManager defaultManager] removeItemAtURL:storeDirectoryURL error:nil];
That recursively removes both the custom sub-directory and all of the Core Data files in it.
This only works if you don't already have your persistent store in the same folder as other, important data. Like the documents directory, which probably has other useful stuff in it. If that's your situation, you could get the same effect by looking for files that you do want to keep and removing everything else. Something like:
NSString *docsDirectoryPath = [[self applicationDocumentsDirectory] path];
NSArray *docsDirectoryContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:docsDirectoryPath error:nil];
for (NSString *docsDirectoryItem in docsDirectoryContents) {
// Look at docsDirectoryItem. If it's something you want to keep, do nothing.
// If it's something you don't recognize, remove it.
}
This approach may be error prone. You've got to be absolutely sure that you know every file you want to keep, because otherwise you might remove important data. On the other hand, you can remove the external binary files without actually knowing the file/directory name used to store them.
Here is combined solution for purging Core Data.
- (void)deleteAllObjectsInCoreData
{
NSArray *allEntities = self.managedObjectModel.entities;
for (NSEntityDescription *entityDescription in allEntities)
{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:entityDescription];
fetchRequest.includesPropertyValues = NO;
fetchRequest.includesSubentities = NO;
NSError *error;
NSArray *items = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (error) {
NSLog(#"Error requesting items from Core Data: %#", [error localizedDescription]);
}
for (NSManagedObject *managedObject in items) {
[self.managedObjectContext deleteObject:managedObject];
}
if (![self.managedObjectContext save:&error]) {
NSLog(#"Error deleting %# - error:%#", entityDescription, [error localizedDescription]);
}
}
}
If you want to delete all objects and do not want to delete the backing files, you can use following methods:
- (void)deleteAllObjectsInContext:(NSManagedObjectContext *)context
usingModel:(NSManagedObjectModel *)model
{
NSArray *entities = model.entities;
for (NSEntityDescription *entityDescription in entities) {
[self deleteAllObjectsWithEntityName:entityDescription.name
inContext:context];
}
}
- (void)deleteAllObjectsWithEntityName:(NSString *)entityName
inContext:(NSManagedObjectContext *)context
{
NSFetchRequest *fetchRequest =
[NSFetchRequest fetchRequestWithEntityName:entityName];
fetchRequest.includesPropertyValues = NO;
fetchRequest.includesSubentities = NO;
NSError *error;
NSArray *items = [context executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject *managedObject in items) {
[context deleteObject:managedObject];
NSLog(#"Deleted %#", entityName);
}
}
Beware that it may be very slow (depends on how many objects are in your object graph).
If you want to go the delete all objects route (which is much simpler than tearing down the Core Data stack, but less performant), than this is a better implementation:
- (void)deleteAllManagedObjectsInModel:(NSManagedObjectModel *)managedObjectModel context:(NSManagedObjectContext *)managedObjectContext
{
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
[managedObjectContext performBlockAndWait:^{
for (NSEntityDescription *entity in managedObjectModel) {
NSFetchRequest *fetchRequest = [NSFetchRequest new];
[fetchRequest setEntity:entity];
[fetchRequest setIncludesSubentities:NO];
NSArray *objects = [managedObjectContext executeFetchRequest:fetchRequest error:nil];
for (NSManagedObject *managedObject in objects) {
[managedObjectContext deleteObject:managedObject];
}
}
[managedObjectContext save:nil];
}];
}];
[operation setCompletionBlock:^{
// Do stuff once the truncation is complete
}];
[operation start];
}
This implementation leverages NSOperation to perform the deletion off of the main thread and notify on completion. You may want to emit a notification or something within the completion block to bubble the status back to the main thread.
iOS 10 + Swift 3 solution:
func clearCoreDataStore() {
let delegate = UIApplication.shared.delegate as! AppDelegate
let context = delegate.persistentContainer.viewContext
for i in 0...delegate.persistentContainer.managedObjectModel.entities.count-1 {
let entity = delegate.persistentContainer.managedObjectModel.entities[i]
do {
let query = NSFetchRequest<NSFetchRequestResult>(entityName: entity.name!)
let deleterequest = NSBatchDeleteRequest(fetchRequest: query)
try context.execute(deleterequest)
try context.save()
} catch let error as NSError {
print("Error: \(error.localizedDescription)")
abort()
}
}
}
Iterates through all of the core data entities and clears them
Swift 4/5, iOS 9+
Rebuilding the whole CoreData SQLite file will make sure all data is erased, therefore all entities are deleted. Just call deleteAndRebuild().
class CoreDataStack {
// Change this
static let datamodelName = "ProjectName"
static let storeType = "sqlite"
static let persistentContainer = NSPersistentContainer(name: datamodelName)
private static let url: URL = {
let url = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0].appendingPathComponent("\(datamodelName).\(storeType)")
assert(FileManager.default.fileExists(atPath: url.path))
return url
}()
static func loadStores() {
persistentContainer.loadPersistentStores(completionHandler: { (nsPersistentStoreDescription, error) in
if let error = error {
fatalError(error.localizedDescription)
}
})
}
static func deleteAndRebuild() {
try! persistentContainer.persistentStoreCoordinator.destroyPersistentStore(at: url, ofType: storeType, options: nil)
loadStores()
}
}
Thanks for the post. I followed it and it worked for me. But I had another issue that was not mentioned in any of the replies. So I am not sure if it was just me.
Anyway, thought I would post here the problem and my way that solved it.
I had a few records in the database, I wanted to purge everything clean before write new data to the db, so I did everything including
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:&error];
and then used managedObjectContext to access the database (supposed to be empty by now), somehow the data was still there.
After a while of troubleshooting, I found that I need to reset managedObjectContext, managedObject, managedObjectModel and
persistentStoreCoordinator, before I use managedObjectContext to access the dabase. Now I have a clean database to write to.
Here is a somewhat simplified version with less calls to AppDelegate self and the last bit of code that was left out of the top rated answer. Also I was getting an error "Object's persistent store is not reachable from this NSManagedObjectContext's coordinator" so just needed to add that back.
NSPersistentStoreCoordinator *storeCoordinator = [self persistentStoreCoordinator];
NSPersistentStore *store = [[storeCoordinator persistentStores] lastObject];
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"dataModel"];
NSError *error;
[storeCoordinator removePersistentStore:store error:&error];
[[NSFileManager defaultManager] removeItemAtPath:storeURL.path error:&error];
[_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error];
if (storeCoordinator != nil) {
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:storeCoordinator];
}
swift solution:
class func deleteAllManagedObjects() {
let modelURL = NSBundle.mainBundle().URLForResource("some string", withExtension: "mom")
let mom = NSManagedObjectModel(contentsOfURL: modelURL)
for entityName in mom.entitiesByName.keys {
let fr = NSFetchRequest(entityName: entityName as String)
let a = Utility.managedObjectContext().executeFetchRequest(fr, error: nil) as [NSManagedObject]
for mo in a {
Utility.managedObjectContext().deleteObject(mo)
}
}
Utility.managedObjectContext().save(nil)
}
As a quick reference to save searching elsewhere - recreating the persistent store after deleting it can be done with:
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
// do something with the error
}
Several good answers to this question. Here's a nice concise one. The first two lines delete the sqlite database. Then the for: loop deletes any objects in the managedObjectContext memory.
NSURL *storeURL = [[(FXYAppDelegate*)[[UIApplication sharedApplication] delegate] applicationDocumentsDirectory] URLByAppendingPathComponent:#"AppName.sqlite"];
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil];
for (NSManagedObject *ct in [self.managedObjectContext registeredObjects]) {
[self.managedObjectContext deleteObject:ct];
}
Here's a version that deletes every record in every table you have.
Swift 4
static func resetDatabase() {
do {
try dataStore.persistentStoreCoordinator.managedObjectModel.entities.forEach { (entity) in
if let name = entity.name {
let fetch = NSFetchRequest<NSFetchRequestResult>(entityName: name)
let request = NSBatchDeleteRequest(fetchRequest: fetch)
try mainContext.execute(request)
}
}
try mainContext.save()
} catch {
print("error resenting the database: \(error.localizedDescription)")
}
}
you can also find all the entity names, and delete them by name. Its a longer version but works well, that way you dont have to work with persistence store
- (void)clearCoreData
{
NSError *error;
NSEntityDescription *des = [NSEntityDescription entityForName:#"Any_Entity_Name" inManagedObjectContext:_managedObjectContext];
NSManagedObjectModel *model = [des managedObjectModel];
NSArray *entityNames = [[model entities] valueForKey:#"name"];
for (NSString *entityName in entityNames){
NSFetchRequest *deleteAll = [NSFetchRequest fetchRequestWithEntityName:entityName];
NSArray *matches = [self.database.managedObjectContext executeFetchRequest:deleteAll error:&error];
}
if (matches.count > 0){
for (id obj in matches){
[_managedObjectContext deleteObject:obj];
}
[self.database.managedObjectContext save:&error];
}
}
for "Any_Entity_Name" just give any one of your entity's name, we only need to figure out the entity description your entities are within. ValueForKey#"name" will return all the entity names. Finally, dont forget to save.
The accepted answer is correct with removing URL by NSFileManager is correct, but as stated in iOS 5+ edit, the persistent store is not represented only by one file. For SQLite store it's *.sqlite, *.sqlite-shm and *.sqlite-wal ... fortunately since iOS 7+ we can use method
[NSPersistentStoreCoordinator
+removeUbiquitousContentAndPersistentStoreAtURL:options:error:]
to take care of removal, so the code should be something like this:
NSPersistentStore *store = ...;
NSError *error;
NSURL *storeURL = store.URL;
NSString *storeName = ...;
NSPersistentStoreCoordinator *storeCoordinator = ...;
[storeCoordinator removePersistentStore:store error:&error];
[NSPersistentStoreCoordinator removeUbiquitousContentAndPersistentStoreAtURL:storeURL.path options:#{NSPersistentStoreUbiquitousContentNameKey: storeName} error:&error];
One other method (apart from a delete batch request) I often use (based on app requirement) is to reset the persistent store. The implementation looks like this for iOS 10+ and Swift (assuming you have a CoreDataManager class):
let persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "<Data-Model-Name>“)
container.loadPersistentStores(completionHandler: { (storeDescription, err) in
if let err = err {
fatalError("loading of store failed: \(err)")
}
})
return container
}()
func resetPersistentStore() {
if let persistentStore = persistentContainer.persistentStoreCoordinator.persistentStores.last {
let storeURL = persistentContainer.persistentStoreCoordinator.url(for: persistentStore)
do {
try persistentContainer.persistentStoreCoordinator.destroyPersistentStore(at: storeURL, ofType: NSSQLiteStoreType, options: nil)
} catch {
print("failed to destroy persistent store:", error.localizedDescription)
}
do {
try persistentContainer.persistentStoreCoordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storeURL, options: nil)
} catch {
print("failed to re-add persistent store:", error.localizedDescription)
}
}
}
One advantage of this method is that it’s more straightforward especially when you have loads of data record for numerous entities in your core data. In which case a delete batch request would be memory intensive.
Swift 5.1 Solution
public static func reset() {
let coordinator = _persistentContainer.persistentStoreCoordinator
for store in coordinator.persistentStores where store.url != nil {
try? coordinator.remove(store)
try? FileManager.default.removeItem(atPath: store.url!.path)
}
}
Works with all versions. Pass entity name and iterate through to delete all the entries and save the context.
func deleteData(entityToFetch: String, completion: #escaping(_ returned: Bool) ->()) {
var context = NSManagedObjectContext()
if #available(iOS 10.0, *) {
context = self.persistentContainer.viewContext
} else {
context = self.managedObjectContext
}
let fetchRequest = NSFetchRequest<NSFetchRequestResult>()
fetchRequest.entity = NSEntityDescription.entity(forEntityName: entityToFetch, in: context)
fetchRequest.includesPropertyValues = false
do {
let results = try context.fetch(fetchRequest) as! [NSManagedObject]
for result in results {
context.delete(result)
}
try context.save()
completion(true)
} catch {
completion(false)
print("fetch error -\(error.localizedDescription)")
}
}
There seems to be two approaches:
Say you have a typical singleton for your core data stack.
import CoreData
public let core = Core.shared
public final class Core {
static let shared = Core()
var container: NSPersistentContainer!
private init() {
container = NSPersistentContainer(name: "stuff")
//deleteSql()
container.loadPersistentStores { storeDescription, error in
if let error = error { print("Error loading... \(error)") }
}
//deleteAll()
}
func saveContext() { // typical save helper
if container.viewContext.hasChanges {
do { try container.viewContext.save()
} catch { print("Error saving... \(error)") }
}
}
then ...
func deleteSql() {
let url = FileManager.default.urls(
for: .applicationSupportDirectory,
in: .userDomainMask)[0].appendingPathComponent( "stuff.sqlite" )
guard FileManager.default.fileExists(atPath: url.path) else {
print("nothing to delete!")
return
}
do {
try container.persistentStoreCoordinator.destroyPersistentStore(
at: url, ofType: "sqlite", options: nil)
print("totally scorched the sql file. you DO now have to LOAD again")
}
catch {
print("there was no sql file there!")
}
}
func deleteAll() { // courtesy #Politta
for e in container.persistentStoreCoordinator.managedObjectModel.entities {
let r = NSBatchDeleteRequest(
fetchRequest: NSFetchRequest(entityName: e.name ?? ""))
let _ = try? container.viewContext.execute(r)
}
saveContext()
print("conventionally deleted everything from within core data. carry on")
}
}
.
1. scorch the sql file
Courtesy the excellent #J.Doe answer. You completely destroy the sql file.
You must to do this
before loading core data, ie, after creating the container, but
before actually loading the stores.)
(Notice the example line of code "//deleteSql()" is just before initialization.)
2. delete everything one by one within core data
Courtesy the excellent #Politta answer. You could do this at any time once core data is up and running.
(Notice the example line of code "//deleteAll()" is after initialization.)
Approach one is probably more useful during development. Approach two is probably more useful in production (in the relatively unusual case that for some reason you need to wipe everything).
Delete the persistent store file and setup a new persistent store coordinator?
Delete sqlite from your fileURLPath and then build.
Assuming you are using MagicalRecord and have a default persistence store:
I don't like all the solutions that assume certain files to exist and/or demand entering the entities names or classes. This is a Swift(2), safe way to delete all the data from all the entities. After deleting it will recreate a fresh stack too (I am actually not sure as to how neccessery this part is).
It's godo for "logout" style situations when you want to delete everything but have a working store and moc to get new data in (once the user logs in...)
extension NSManagedObject {
class func dropAllData() {
MagicalRecord.saveWithBlock({ context in
for name in NSManagedObjectModel.MR_defaultManagedObjectModel().entitiesByName.keys {
do { try self.deleteAll(name, context: context) }
catch { print("⚠️ ✏️ Error when deleting \(name): \(error)") }
}
}) { done, err in
MagicalRecord.cleanUp()
MagicalRecord.setupCoreDataStackWithStoreNamed("myStoreName")
}
}
private class func deleteAll(name: String, context ctx: NSManagedObjectContext) throws {
let all = NSFetchRequest(entityName: name)
all.includesPropertyValues = false
let allObjs = try ctx.executeFetchRequest(all)
for obj in allObjs {
obj.MR_deleteEntityInContext(ctx)
}
}
}
Use this
+(NSArray *)fetchDataFromEntity:(NSString *)entityName context:(NSManagedObjectContext *)context
{
NSFetchRequest * fetchRequest =[[NSFetchRequest alloc] init];
NSEntityDescription * CategoriesEntity = [NSEntityDescription entityForName:entityName inManagedObjectContext:context];
[fetchRequest setEntity:CategoriesEntity];
NSError * error;
NSInteger count = [context countForFetchRequest:fetchRequest error:&error];
if (count && count>0) {
NSArray * fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects && fetchedObjects.count>0) {
return fetchedObjects;
}else
return nil;
}
else
return nil;
}
+ (void)deleteObjectsOfArray:(NSMutableArray*)ary context:(NSManagedObjectContext *)context {
for (NSManagedObject * obj in ary) {
[context deleteObject:obj];
}
NSError *saveError = nil;
[context save:&saveError];
}
+ (void)deleteEntity:(NSString *)entityName context:(NSManagedObjectContext *)context {
NSArray *listArray = [self fetchDataFromEntity:entityName context:context];
[self deleteObjectsOfArray:[NSMutableArray arrayWithArray:listArray] context:context];
}
I took Grouchal's code and to speed it up I used enumeration with concurrent mode (NSEnumerationConcurrent), it got a bit faster compared to for loop (in my app I added this feature for Testers so that they can clear data and do testcases rather than delete and install app)
- (void)resetObjects
{
[self deleteAllObjectsInEntity:#"Entity1"];
[self deleteAllObjectsInEntity:#"Entity2"];
[self deleteAllObjectsInEntity:#"Entity3"];
[self deleteAllObjectsInEntity:#"Entity4"];
}
-(void) deleteAllObjectsInEntity:(NSString*) entityName
{
MainDataContext *coreDataContext = [MainDataContext sharedInstance];
NSManagedObjectContext *currentContext = coreDataContext.managedObjectContext;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:currentContext];
[fetchRequest setEntity:entity];
NSError *error;
NSArray *items = [currentContext executeFetchRequest:fetchRequest error:&error];
[items enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(NSManagedObject * obj, NSUInteger idx, BOOL *stop) {
[currentContext deleteObject:obj];
}];
if (![currentContext save:&error]) {
NSLog(#"Error deleting %# - error:%#",entityName,error);
}
}
Related
I'm entirly new to creating iOS apps. I have to quickly create a form-application where I can store information from people willing to fill it in. Basicly just a bunch of text-fields for stuff like name, mail etc.
Once the form is filled, I'm storing their data with this bit of code:
//Save action
NSEntityDescription *entityDesc = [NSEntityDescription entityForName:#"Person" inManagedObjectContext: context];
NSManagedObject *newPerson = [[NSManagedObject alloc]initWithEntity:entityDesc insertIntoManagedObjectContext:context];
//Fill in values
[newPerson setValue:self.btnPrefix.titleLabel.text forKey: #"prefix"];
[newPerson setValue:self.txtFirstName.text forKey: #"firstname"];
[newPerson setValue:self.txtLastName.text forKey: #"lastname"];
[newPerson setValue:self.txtLive.text forKey: #"country"];
[newPerson setValue:self.txtMail.text forKey: #"email"];
[newPerson setValue:self.txtPhone.text forKey: #"phonenumber"];
[newPerson setValue:self.txtLinked.text forKey: #"linkedIn"];
[newPerson setValue:self.txtAbout.text forKey: #"about"];
NSError *error;
[context save:&error];
When executed on the simulator no problem at all. But once ran on the iPad I get this error:
Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '+entityForName: nil is not a
legal NSManagedObjectContext parameter searching for entity name
'Person''
After debugging it gets triggerd on the first line:
NSEntityDescription *entityDesc = [NSEntityDescription entityForName:#"Person" inManagedObjectContext: context];
After a bit more research, my Appdelegate contains a nil persistentContainer when ran on device, but it's filled when ran on virtual device. So I guess the problem is there, but I can't find a way to solve it.
- (void)viewDidLoad {
[super viewDidLoad];
AppDelegate *appdelegate = (AppDelegate*)[[UIApplication sharedApplication]delegate];
context = appdelegate.persistentContainer.viewContext;
}
Can anyone help me out?
For those encountering this error. Most probably if it works on the virtual device but not on the physical device it's due to the differences for accessing core data between iOS 9 and 10.
In Xcode 8 the AppDelegate automaticly generates data for iOS 10 but if you're stuck on iOS 9 you'll need to add the following code in your delegate file:
#synthesize managedObjectContext = _managedObjectContext;
#synthesize managedObjectModel = _managedObjectModel;
#synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
- (NSURL *) applicationDocumentsDirectory{
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
- (NSManagedObjectModel *)managedObjectModel {
if(_managedObjectModel != nil){
return _managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"StylelabsForms" withExtension:#"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
}
- (NSPersistentStoreCoordinator *) persistentStoreCoordinator{
if(_persistentStoreCoordinator != nil){
return _persistentStoreCoordinator;
}
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"StylelabsForms.sqlite"];
NSError *error = nil;
NSString *failureReason = #"Error loading saved data";
if(![_persistentStoreCoordinator addPersistentStoreWithType: NSSQLiteStoreType configuration:nil URL: storeURL options:nil error:&error]){
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
dict[NSLocalizedDescriptionKey] = #"Failed init application's saved data";
dict[NSLocalizedFailureReasonErrorKey] = failureReason;
dict[NSUnderlyingErrorKey] = error;
error = [NSError errorWithDomain:#"YOUR_ERROR_DOMAIN" code:9999 userInfo:dict];
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return _persistentStoreCoordinator;
}
- (NSManagedObjectContext *) managedObjectContext {
if (_managedObjectContext != nil){
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if(!coordinator) {
return nil;
}
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
return _managedObjectContext;
}
Also adapt the save as followed:
- (void)saveContext {
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if(managedObjectContext != nil){
NSError *error = nil;
if([managedObjectContext hasChanges] && ![managedObjectContext save:&error]){
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
//For iOS 10 and above
/*
NSManagedObjectContext *context = self.persistentContainer.viewContext;
NSError *error = nil;
if ([context hasChanges] && ![context save:&error]) {
// Replace this implementation 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();
} */
}
private lazy var applicationDocumentsDirectory: URL = {
// The directory the application uses to store the Core Data store file. This code uses a directory named in the application's documents Application Support directory.
let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return urls[urls.count-1]
}()
private lazy var managedObjectModel: NSManagedObjectModel = {
// The managed object model for the application. This property is not optional. It is a fatal error for the application not to be able to find and load its model.
let modelURL = Bundle.main.url(forResource: "CoreData", withExtension: "momd")!
return NSManagedObjectModel(contentsOf: modelURL)!
}()
private lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
// The persistent store coordinator for the application. This implementation creates and returns 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
let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
let url = self.applicationDocumentsDirectory.appendingPathComponent("CoreData.sqlite")
var failureReason = "There was an error creating or loading the application's saved data."
do {
// Configure automatic migration.
let options = [ NSMigratePersistentStoresAutomaticallyOption : true, NSInferMappingModelAutomaticallyOption : true ]
try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: options)
} catch {
// Report any error we got.
var dict = [String: AnyObject]()
dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data" as AnyObject?
dict[NSLocalizedFailureReasonErrorKey] = failureReason as AnyObject?
dict[NSUnderlyingErrorKey] = error as NSError
let wrappedError = 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 \(wrappedError), \(wrappedError.userInfo)")
abort()
}
return coordinator
}()
lazy var managedObjectContext: NSManagedObjectContext = {
var managedObjectContext: NSManagedObjectContext?
if #available(iOS 10.0, *){
managedObjectContext = self.persistentContainer.viewContext
}
else{
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail.
let coordinator = self.persistentStoreCoordinator
managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
managedObjectContext?.persistentStoreCoordinator = coordinator
}
return managedObjectContext!
}()
// iOS-10
#available(iOS 10.0, *)
lazy var persistentContainer: NSPersistentContainer = {
/*
The persistent container for the application. This implementation
creates and returns a container, having loaded 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.
*/
let container = NSPersistentContainer(name: "CoreData")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() 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.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error)")
}
})
print("\(self.applicationDocumentsDirectory)")
return container
}()
anyone can describe me about core data?
I want to create worksheet which store day activity record and that data stored in local file.
I think core data is best to store locally.
Thanks in advance.
You should see CoreData not as a database, but as a way to manage a graph of object.
You can then store this graph in different places (transparently from the application point of view) such as memory, XML, sqlite, and I think custom binary file.
What you usually do is to write the model in a core data model.
Each object is either an instance of NSManagedObject (which you can query / work with with methods such as valueForKey:, setValueForKey: etc) or subclasses of that class. This subclasses can be autogenerated directly from Xcode, and at this point you almost forget you are working with CoreData. Every attribute is a #property, every to-many relationship is a NSSet.
You get back to the fact that you are using CoreData when you create and want to save the object. In this case you have to get the 'context' in which the object resides, and call method on it (e.g. save)
There is full of tutorial and documentation on the web about CoreData.
In my opinion the core point is.. don't think at it as a relational database. "Be more object oriented" :)
To getting started you can take a look at:
http://www.raywenderlich.com/934/core-data-tutorial-for-ios-getting-started
The for more complex stuff the apple doc is ok
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreData/cdProgrammingGuide.html#//apple_ref/doc/uid/TP30001200-SW1
Yes you are right, Core data is bast way to store data in iOS applications.
With new XCode all you need to do is when creating new project click check box that you will use coreData.
This will create yourPorject.xcdatamodeld file and some methods inside your AppDelegate file :
- (NSURL *)applicationDocumentsDirectory {
// The directory the application uses to store the Core Data store file. This code uses a directory named "result.PingPong" in the application's documents directory.
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
- (NSManagedObjectModel *)managedObjectModel {
// The managed object model for the application. It is a fatal error for the application not to be able to find and load its model.
if (_managedObjectModel != nil) {
return _managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"PingPong" withExtension:#"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
// The persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it.
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
// Create the coordinator and store
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"PingPong.sqlite"];
NSError *error = nil;
NSString *failureReason = #"There was an error creating or loading the application's saved data.";
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
// Report any error we got.
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
dict[NSLocalizedDescriptionKey] = #"Failed to initialize the application's saved data";
dict[NSLocalizedFailureReasonErrorKey] = failureReason;
dict[NSUnderlyingErrorKey] = error;
error = [NSError errorWithDomain:#"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 _persistentStoreCoordinator;
}
- (NSManagedObjectContext *)managedObjectContext {
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.)
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (!coordinator) {
return nil;
}
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
return _managedObjectContext;
}
#pragma mark - Core Data Saving support
- (void)saveContext {
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil) {
NSError *error = nil;
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
// Replace this implementation 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();
}
}
}
(I case it didn't)
After that you create your entity object inside yourPorject.xcdatamodeld, by clicking on AddEntity, name it and inside with Add Attribute add all your attributes.
After that click on menu : Editor -> Create NSManagedObject subclasses. This will automatically create object for you.
All you need to do to save object into database is
AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
NSManagedObjectContext *context = appDelegate.managedObjectContext;
YourObject * o = [NSEntityDescription insertNewObjectForEntityForName:#"YourObject" inManagedObjectContext:context];
o.attribute1 = attribute1;
o.attribute2 = attribute2;
[context save:nil];
To fetch all object you will need this :
AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
NSManagedObjectContext *context = appDelegate.managedObjectContext;
NSError *fetchError;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"YourObject" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&fetchError];
if (fetchError != nil) {
NSLog(#"Error fetching database request. Reason: %#", fetchError);
}
I hope it will help you for start.
Marko
I am using CoreData in an iOS application to manage words in a "flash cards" application for people learning new languages.
The problem I'm having is that when I set up the data for a new entity and try to save it to the data store, I get a violation of a UNIQUE CONSTRAINT requirement on the sqlite database. The column in question is the Z_PK column, which I understand to be a PRIMARY KEY created by the iOS core data methods when the data store is initially created.
Here's the UNIQUE CONSTRAINT message I'm getting when I attempt the save:
2015-03-14 09:25:14.427 ghoti[25856:1107373] CoreData: error: (1555)
UNIQUE constraint failed: ZGHOTIENTITY.Z_PK (lldb)
Z is the prefix stuck on all of these SQL columns
GHOTIENTITY is my data store
Z_PK is the primary key
Here's a quick screenshot of the data table inside the sqlite "editor" in firefox:
The definition of the business object entity itself does not include the Z_PK column:
#implementation ghotiEntity
#dynamic numberCorrect;
#dynamic numberIncorrect;
#dynamic likelihoodOfPresentingWord;
#dynamic englishText;
#dynamic hebrewText;
#dynamic phoneticText;
#end
I'd be annoyed - but compliant - if it were as simple as just setting the ghotiEntity.Z_PK to the maximum value of Z_PK in the datastore + 1, but that column is not even part of the entity definition in the first place.
EDIT - A helpful user asked for the code, so I'm adding it here rather than trying to stuff it into a comment:
I'm using the mmBusinessObject collection of core data methods that I've seen floating around the web. Kevin McNeish explains it fully here.
The basic structure is this:
mmBusinessObject.m and .h do a nice job of wrapping the managedObject methods into something "easy to read and deal with". For the sake of readability, I've copied the snippet of two elements [entity createEntities] and [entity saveEntities] here and the full .m file at the end of this post. (note that I have not yet built in all of the error-checking, I'm just trying to make the basic function work.
CREATE A NEW ENTITY:
// Creates a new entity of the default type and adds it to the managed object context
- (NSManagedObject *)createEntity
{
return [NSEntityDescription insertNewObjectForEntityForName:self.entityClassName inManagedObjectContext:[self managedObjectContext]];
}
SAVE THE CHANGES
- (void)saveEntities
{
NSError *error = nil;
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil) {
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
// Replace this implementation 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();
}
}
}
So my operating code (inside my ViewController.m file) does this: (note that ghotiEntity is my specific business object.
- (IBAction)saveButtonAction:(UIButton *)sender {
ghotiEntity *newGhotiEntry = (ghotiEntity *)[ghotiData createEntity];
newGhotiEntry.englishText=self.englishWord.text;
newGhotiEntry.hebrewText=self.hebrewWord.text;
newGhotiEntry.phoneticText=self.phoneticWord.text;
newGhotiEntry.numberCorrect=0;
newGhotiEntry.numberIncorrect=0;
newGhotiEntry.likelihoodOfPresentingWord=0;
[ghotiData saveEntities];
[self enableRegularUI];
[self pickNextCard];
}
It is from the NSLog line in the saveEntities method above that I get the diagnostic information that it is the Z_PK column that has the UNIQUE CONSTRAINT problem.
As promised above, here is the full .m file. I need to make sure that it is clear that the credit for this code goes to either Kevin McNeish as noted above, or any of several other collaborators who have posted the same code in other places... just search mmBusinessObject in google. I think Kevin is the originator and his tutorial is really good. The first few lines in the versions I have seen all have Kevin's name in them!
#import "mmBusinessObject.h"
#implementation mmBusinessObject
// Initialization
- (id)init
{
if ((self = [super init])) {
_copyDatabaseIfNotPresent = YES;
}
return self;
}
// Creates a new entity of the default type and adds it to the managed object context
- (NSManagedObject *)createEntity
{
return [NSEntityDescription insertNewObjectForEntityForName:self.entityClassName inManagedObjectContext:[self managedObjectContext]];
}
// Delete the specified entity
- (void) deleteEntity:(NSManagedObject *)entity {
[self.managedObjectContext deleteObject:entity];
}
// Gets entities for the specified request
- (NSMutableArray *)getEntities: (NSString *)entityName sortedBy:(NSSortDescriptor *)sortDescriptor matchingPredicate:(NSPredicate *)predicate
{
NSError *error = nil;
// Create the request object
NSFetchRequest *request = [[NSFetchRequest alloc] init];
// Set the entity type to be fetched
NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:[self managedObjectContext]];
[request setEntity:entity];
// Set the predicate if specified
if (predicate) {
[request setPredicate:predicate];
}
// Set the sort descriptor if specified
if (sortDescriptor) {
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
}
// Execute the fetch
NSMutableArray *mutableFetchResults = [[_managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (mutableFetchResults == nil) {
// Handle the error.
}
return mutableFetchResults;
}
// Gets all entities of the default type
- (NSMutableArray *)getAllEntities
{
return [self getEntities:self.entityClassName sortedBy:nil matchingPredicate:nil];
}
// Gets entities of the default type matching the predicate
- (NSMutableArray *)getEntitiesMatchingPredicate: (NSPredicate *)predicate
{
return [self getEntities:self.entityClassName sortedBy:nil matchingPredicate:predicate];
}
// Gets entities of the default type matching the predicate string
- (NSMutableArray *)getEntitiesMatchingPredicateString: (NSString *)predicateString, ...;
{
va_list variadicArguments;
va_start(variadicArguments, predicateString);
NSPredicate *predicate = [NSPredicate predicateWithFormat:predicateString
arguments:variadicArguments];
va_end(variadicArguments);
return [self getEntities:self.entityClassName sortedBy:nil matchingPredicate:predicate];
}
// Get entities of the default type sorted by descriptor matching the predicate
- (NSMutableArray *)getEntitiesSortedBy: (NSSortDescriptor *) sortDescriptor
matchingPredicate:(NSPredicate *)predicate
{
return [self getEntities:self.entityClassName sortedBy:sortDescriptor matchingPredicate:predicate];
}
// Gets entities of the specified type sorted by descriptor, and matching the predicate string
- (NSMutableArray *)getEntities: (NSString *)entityName
sortedBy:(NSSortDescriptor *)sortDescriptor
matchingPredicateString:(NSString *)predicateString, ...;
{
va_list variadicArguments;
va_start(variadicArguments, predicateString);
NSPredicate *predicate = [NSPredicate predicateWithFormat:predicateString
arguments:variadicArguments];
va_end(variadicArguments);
return [self getEntities:entityName sortedBy:sortDescriptor matchingPredicate:predicate];
}
- (void) registerRelatedObject:(mmBusinessObject *)controllerObject
{
controllerObject.managedObjectContext = self.managedObjectContext;
}
// Saves all changes (insert, update, delete) of entities
- (void)saveEntities
{
NSError *error = nil;
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil) {
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
// Replace this implementation 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();
}
}
}
#pragma mark -
#pragma mark Core Data stack
/**
Returns the managed object context for the application.
If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
*/
- (NSManagedObjectContext *)managedObjectContext {
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return _managedObjectContext;
}
/**
Returns the managed object model for the application.
If the model doesn't already exist, it is created from the application's model.
*/
- (NSManagedObjectModel *)managedObjectModel {
if (_managedObjectModel != nil) {
return _managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:self.dbName withExtension:#"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
}
/**
Returns the persistent store coordinator for the application.
If the coordinator doesn't already exist, it is created and the application's store added to it.
*/
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
// If the sqlite database doesn't already exist, create it
// by copying the sqlite database included in this project
if (self.copyDatabaseIfNotPresent) {
// Get the documents directory
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *docsDir = paths[0];
// Append the name of the database to get the full path
NSString *dbcPath = [docsDir stringByAppendingPathComponent:
[self.dbName stringByAppendingString:#".sqlite"]];
// Create database if it doesn't already exist
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:dbcPath]) {
NSString *defaultStorePath = [[NSBundle mainBundle]
pathForResource:self.dbName ofType:#"sqlite"];
if (defaultStorePath) {
[fileManager copyItemAtPath:defaultStorePath toPath:dbcPath error:NULL];
}
}
}
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:
[NSString stringWithFormat:#"%#%#", self.dbName, #".sqlite"]];
NSError *error = nil;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
/*
Replace this implementation 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. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
Typical reasons for an error here include:
* The persistent store is not accessible;
* The schema for the persistent store is incompatible with current managed object model.
Check the error message to determine what the actual problem was.
If the persistent store is not accessible, there is typically something wrong with the file path. Often, a file URL is pointing into the application's resources directory instead of a writeable directory.
If you encounter schema incompatibility errors during development, you can reduce their frequency by:
* Simply deleting the existing store:
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil]
* Performing automatic lightweight migration by passing the following dictionary as the options parameter:
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES],NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
Lightweight migration will only work for a limited set of schema changes; consult "Core Data Model Versioning and Data Migration Programming Guide" for details.
*/
NSLog(#"%#",storeURL);
if ([error code] == 134100) {
[self performAutomaticLightweightMigration];
}
else
{
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
return _persistentStoreCoordinator;
}
- (void)performAutomaticLightweightMigration {
NSError *error;
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:[NSString stringWithFormat:#"%#%#", self.dbName, #".sqlite"]];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:storeURL
options:options
error:&error]){
// Handle the error.
}
}
#pragma mark -
#pragma mark Application's Documents directory
/**
Returns the URL to the application's Documents directory.
*/
- (NSURL *)applicationDocumentsDirectory {
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
#end
I had the same problem with a Z_PK constraint issue. It stemmed from the fact that I had copied the empty sqlite database from my app's documents folder and prepopulated it using Firefox's SQLite manager add-in. The step I forgot was updating the Z_PrimaryKey table...you have to update the Z_MAX value for each table so that Core Data will know the next Z_PK to use when new records are added to the database. Simply set each Z_Max value with the number of records in each table. Save it and drop it back into your app and all will be well.
Look through your code for all places you create an object for the DB. Make sure that you are not creating objects from multiple threads.
i had the same issue but i had this when i want to update an entry , so i fixed with refreshObject then save like this:
[dataManager.managedObjectContext refreshObject:object mergeChanges:YES];
[dataManager saveContextWithBlock:block];
I have Core Data working in my app. So, I fetch an XML file, parse the data into model objects and insert them into core data. They are saved in the persistent store and I can access them when I relaunch the app. However, I want to be able to refresh the data in the persistent store at will, so I need to first remove existing objects from the store. Is there a straight-forward method for this?
Thanks
I found this solution:
[managedObjectContext lock];
[managedObjectContext reset];//to drop pending changes
if ([persistentStoreCoordinator removePersistentStore:persistentStore error:&error])
{
NSURL* storeURL = [NSURL fileURLWithPath:[self pathForPersistentStore]];
[[NSFileManager defaultManager] removeFileAtPath:[storeURL path] handler:nil];
[self addPersistentStore];//recreates the persistent store
}
[managedObjectContext unlock];
Here's what I have done to clean my Core Data entirely. It works perfectly. This is if you only have one persistent store which is probably the case if you didn't add one more manually. If your managedObjectContext has the same name as here you can simply copy/past it will work.
NSError * error;
// retrieve the store URL
NSURL * storeURL = [[managedObjectContext persistentStoreCoordinator] URLForPersistentStore:[[[managedObjectContext persistentStoreCoordinator] persistentStores] lastObject]];
// lock the current context
[managedObjectContext lock];
[managedObjectContext reset];//to drop pending changes
//delete the store from the current managedObjectContext
if ([[managedObjectContext persistentStoreCoordinator] removePersistentStore:[[[managedObjectContext persistentStoreCoordinator] persistentStores] lastObject] error:&error])
{
// remove the file containing the data
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:&error];
//recreate the store like in the appDelegate method
[[managedObjectContext persistentStoreCoordinator] addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error];//recreates the persistent store
}
[managedObjectContext unlock];
//that's it !
swift version of #Nicolas Manzini answer:
if let psc = self.managedObjectContext?.persistentStoreCoordinator{
if let store = psc.persistentStores.last as? NSPersistentStore{
let storeUrl = psc.URLForPersistentStore(store)
self.managedObjectContext?.performBlockAndWait(){
self.managedObjectContext?.reset()
var error:NSError?
if psc.removePersistentStore(store, error: &error){
NSFileManager.defaultManager().removeItemAtURL(storeUrl, error: &error)
psc.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeUrl, options: nil, error: &error)
}
}
}
}
Based on #Nicolas Manzini answer I have wrote a Swift 2.1 version with little improvements. I have added an extension to NSManagedObjectContext. Full code below:
import Foundation
import CoreData
extension NSManagedObjectContext
{
func deleteAllData()
{
guard let persistentStore = persistentStoreCoordinator?.persistentStores.last else {
return
}
guard let url = persistentStoreCoordinator?.URLForPersistentStore(persistentStore) else {
return
}
performBlockAndWait { () -> Void in
self.reset()
do
{
try self.persistentStoreCoordinator?.removePersistentStore(persistentStore)
try NSFileManager.defaultManager().removeItemAtURL(url)
try self.persistentStoreCoordinator?.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: nil)
}
catch { /*dealing with errors up to the usage*/ }
}
}
}
There is a function
According to WWDC 242, you can use it for clearing a database.
Also there is a func replacePersistentStore which is replacing the current database with a selected one.
You could loop through all objects and delete them by doing this:
[managedObjectContext deleteObject:someObject];
If you want to remove all objects it is probably fastest to delete the store and then recreate the CoreData stack.
Trash your data file and remake it.
import Foundation
import CoreData
extension NSManagedObjectContext
{
func deleteAllData() {
guard let persistentStore = persistentStoreCoordinator?.persistentStores.last else {
return
}
guard let url = persistentStoreCoordinator?.url(for: persistentStore) else {
return
}
performAndWait { () -> Void in
self.reset()
do
{
try self.persistentStoreCoordinator?.remove(persistentStore)
try FileManager.default.removeItem(at: url)
try self.persistentStoreCoordinator?.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: nil)
}
catch { /*dealing with errors up to the usage*/ }
}
}
}
Thanks #Julian Krol - updated answer for Swift 5.1
The fastest way to ditch everything is to send your managed object context the reset message.
I've been creating a list app and backing it with core data.
I would like to have a default list of say 10 airport's items, so that the user doesn't have to start from scratch.
Is there any way to do this?
Any help is appreciated.
Thanks in advance.
Here's the best way (and doesn't require SQL knowledge):
Create a quick Core Data iPhone app (Or even Mac app) using the same object model as your List app. Write a few lines of code to save the default managed objects you want to the store. Then, run that app in the simulator. Now, go to ~/Library/Application Support/iPhone Simulator/User/Applications. Find your application among the GUIDs, then just copy the sqlite store out into your List app's project folder.
Then, load that store like they do in the CoreDataBooks example.
Yes there is in fact the CoreDataBooks example does this, you can download the code here: sample code
What you do is create the internal store (database) using the normal procedure to initialize your store just like you would with any other store, then you simply run your code and let it execute the code as described in the CoreDataBooks example (code snippet below). Once the store has been initialized you will want to create a NSManagedObjectContext and initialize it with the created persistent store, insert all the entities you need, and save the context.
Once the context has been successfully saved, you can stop your application, then go to finder and go to folder: ~/Library/Developer type in the search .sqlite and look under /Developer, sorting by date will give you the most recent .sqlite database which should match the time that the code was executed, you can then take this store and add it as a resource of your project. This file then can be read by a persistent store coordinator.
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator) {
return persistentStoreCoordinator;
}
NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent: #"CoreDataBooks.sqlite"];
/*
Set up the store.
For the sake of illustration, provide a pre-populated default store.
*/
NSFileManager *fileManager = [NSFileManager defaultManager];
// If the expected store doesn't exist, copy the default store.
if (![fileManager fileExistsAtPath:storePath]) {
NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:#"CoreDataBooks" ofType:#"sqlite"];
if (defaultStorePath) {
[fileManager copyItemAtPath:defaultStorePath toPath:storePath error:NULL];
}
}
NSURL *storeUrl = [NSURL fileURLWithPath:storePath];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
NSError *error;
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error]) {
// Update to handle the error appropriately.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
exit(-1); // Fail
}
return persistentStoreCoordinator;
}
Hope that helps.
-Oscar
With this method you don't need to make a separate app or have any SQL knowledge. You only need to be able to make a JSON file for your initial data.
I use a JSON file that I parse into objects, then insert them in Core Data. I do this when the app initializes. I also make one entity in my core data that indicates if this initial data is already inserted, after I insert the initial data I set this entity so the next time the script runs it sees that the initial data has already been initialized.
To read json file into objects:
NSString *initialDataFile = [[NSBundle mainBundle] pathForResource:#"InitialData" ofType:#"json"];
NSError *readJsonError = nil;
NSArray *initialData = [NSJSONSerialization
JSONObjectWithData:[NSData dataWithContentsOfFile:initialDataFile]
options:kNilOptions
error:&readJsonError];
if(!initialData) {
NSLog(#"Could not read JSON file: %#", readJsonError);
abort();
}
Then you can make entity objects for it like this:
[initialData enumerateObjectsUsingBlock:^(id objData, NSUInteger idx, BOOL *stop) {
MyEntityObject *obj = [NSEntityDescription
insertNewObjectForEntityForName:#"MyEntity"
inManagedObjectContext:dataController.managedObjectContext];
obj.name = [objData objectForKey:#"name"];
obj.description = [objData objectForKey:#"description"];
// then insert 'obj' into Core Data
}];
If you want a more detailed description on how to do this, check out this tutorial:
http://www.raywenderlich.com/12170/core-data-tutorial-how-to-preloadimport-existing-data-updated
For 10 items, you can just do this within applicationDidFinishLaunching: in your app delegate.
Define a method, say insertPredefinedObjects, that creates and populates the instances of the entity in charge of managing your airport items, and save your context. You may either read the attributes from a file or simply hardwire them in your code. Then, call this method inside applicationDidFinishLaunching:.
Bear in mind, when following the CoreDataBooks example code, that it probably breaks the iOS Data Storage Guidelines:
https://developer.apple.com/icloud/documentation/data-storage/
I've had an app rejected for copying the (read-only) pre-populated database to the documents directory - as it then gets backed up to iCloud - and Apple only want that to happen to user-generated files.
The guidelines above offer some solutions, but they mostly boil down to:
store the DB in the caches directory, and gracefully handle situations where the OS purges the caches - you will have to rebuild the DB, which probably rules it out for most of us.
set a 'do not cache attribute' on the DB file, which is a little arcane, as it needs to be done differently for different OS versions.
I don't think it is too tricky, but be aware that you have a bit extra to do to make that example code work alongside iCloud...
This answer is only for people who are
including a pre-populated database in your app
making an app for multiple platforms (iOS, Android, etc.)
I had made a prepopulated SQLite database for an Android app. Then when I was making an iOS version of the app I thought it would be best to use Core Data. So I spent quite a long time learning Core Data and then rewriting the code to prepopulate the database. Learning how to do every single step in both platforms required lots of research and trial and error. There was a lot less overlap than I would have hoped.
In the end I just decided to use the same SQLite database from my Android project. Then I used the FMDB wrapper to directly access the database in iOS. The benefits:
Only need to make the prepopulated database once.
Doesn't require a paradigm shift. The syntax between Android and FMDB, while different, is still fairly similar.
Have a lot more control over how Queries are performed.
Allows full text search.
Although I don't regret learning Core Data, if I were to do it over I could have saved a lot of time by just sticking to SQLite.
If you are starting in iOS and then planning to move to Android, I would still use a SQLite wrapper like FMDB or some other software to prepopulate the database. Although you can technically extract the SQLite database that you prepopulate with Core Data, the schema (table and column names, etc.) will be strangely named.
By the way, if you don't need to modify your prepopulated database, then don't copy it to the documents directory after the app is installed. Just access it directly from the bundle.
// get url reference to databaseName.sqlite in the bundle
let databaseURL: NSURL = NSBundle.mainBundle().URLForResource("databaseName", withExtension: "sqlite")!
// convert the url to a path so that FMDB can use it
let database = FMDatabase(path: databaseURL.path)
This makes it so that you don't have two copies.
Update
I now use SQLite.swift rather than FMDB, because it integrates better with Swift projects.
This worked for me. This is a modification of this answer by Andrea Toso and inspired by this blog. The only issue with the answer is that there is a chance of data loss when moving sqlite files with FileManager. I saved around 500 rows of data by using replacePersistentStore instead of FileManager.default.copyItem
Step 1
Populate your Core Data in another app and get files' path using this code:
let paths = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
print(documentsDirectory)
Step2
Drag your 3 files with .sqlite extension into your xCode project. (Be sure to select Add to targets option).
Step3
Create function to check app's first run in AppDelegate.swift
func isFirstLaunch() -> Bool {
let hasBeenLaunchedBeforeFlag = "hasBeenLaunchedBeforeFlag"
let isFirstLaunch = !UserDefaults.standard.bool(forKey: hasBeenLaunchedBeforeFlag)
if (isFirstLaunch) {
UserDefaults.standard.set(true, forKey: hasBeenLaunchedBeforeFlag)
UserDefaults.standard.synchronize()
}
return isFirstLaunch
}
Step4
Copy this function in AppDelegate.swift to get url where sqlite database should be moved:
func getDocumentsDirectory()-> URL {
let paths = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
return documentsDirectory
}
Step 5
Replace declaration of persistentContainer with this one:
// MARK: - Core Data stack
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "ProjectName")
let storeUrl = self.getDocumentsDirectory().appendingPathComponent("FileName.sqlite")
if isFirstLaunch() {
let seededDataUrl = Bundle.main.url(forResource: "FileName", withExtension: "sqlite")
try! container.persistentStoreCoordinator.replacePersistentStore(at: storeUrl, destinationOptions: nil, withPersistentStoreFrom: seededDataUrl!, sourceOptions: nil, ofType: NSSQLiteStoreType)
}
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
So I have developed a generic method that loads from a dictionary (possibly from JSON) and populates the database.
It should be used ONLY with trusted data (from a safe channel), it can't handle circular references and schema migrations can be problematic... But for simple use cases like mine it should be fine
Here it goes
- (void)populateDBWithDict:(NSDictionary*)dict
withContext:(NSManagedObjectContext*)context
{
for (NSString* entitieName in dict) {
for (NSDictionary* objDict in dict[entitieName]) {
NSManagedObject* obj = [NSEntityDescription insertNewObjectForEntityForName:entitieName inManagedObjectContext:context];
for (NSString* fieldName in objDict) {
NSString* attName, *relatedClass, *relatedClassKey;
if ([fieldName rangeOfString:#">"].location == NSNotFound) {
//Normal attribute
attName = fieldName; relatedClass=nil; relatedClassKey=nil;
} else {
NSArray* strComponents = [fieldName componentsSeparatedByString:#">"];
attName = (NSString*)strComponents[0];
relatedClass = (NSString*)strComponents[1];
relatedClassKey = (NSString*)strComponents[2];
}
SEL selector = NSSelectorFromString([NSString stringWithFormat:#"set%#:", attName ]);
NSMethodSignature* signature = [obj methodSignatureForSelector:selector];
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setTarget:obj];
[invocation setSelector:selector];
//Lets set the argument
if (relatedClass) {
//It is a relationship
//Fetch the object
NSFetchRequest* query = [NSFetchRequest fetchRequestWithEntityName:relatedClass];
query.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:relatedClassKey ascending:YES]];
query.predicate = [NSPredicate predicateWithFormat:#"%K = %#", relatedClassKey, objDict[fieldName]];
NSError* error = nil;
NSArray* matches = [context executeFetchRequest:query error:&error];
if ([matches count] == 1) {
NSManagedObject* relatedObject = [matches lastObject];
[invocation setArgument:&relatedObject atIndex:2];
} else {
NSLog(#"Error! %# = %# (count: %d)", relatedClassKey,objDict[fieldName],[matches count]);
}
} else if ([objDict[fieldName] isKindOfClass:[NSString class]]) {
//It is NSString
NSString* argument = objDict[fieldName];
[invocation setArgument:&argument atIndex:2];
} else if ([objDict[fieldName] isKindOfClass:[NSNumber class]]) {
//It is NSNumber, get the type
NSNumber* argument = objDict[fieldName];
[invocation setArgument:&argument atIndex:2];
}
[invocation invoke];
}
NSError *error;
if (![context save:&error]) {
NSLog(#"%#",[error description]);
}
}
}
}
And loads from json...
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"initialDB" ofType:#"json"];
NSData *jsonData = [NSData dataWithContentsOfFile:filePath];
NSError* error;
NSDictionary *initialDBDict = [NSJSONSerialization JSONObjectWithData:jsonData
options:NSJSONReadingMutableContainers error:&error];
[ self populateDBWithDict:initialDBDict withContext: [self managedObjectContext]];
JSON examples
{
"EntitieA": [ {"Att1": 1 }, {"Att1": 2} ],
"EntitieB": [ {"Easy":"AS ABC", "Aref>EntitieA>Att1": 1} ]
}
and
{
"Country": [{"Code": 55, "Name": "Brasil","Acronym": "BR"}],
"Region": [{"Country>Country>code": 55, "Code": 11, "Name": "Sao Paulo"},
{"Country>Country>code": 55, "Code": 31, "Name": "Belo Horizonte"}]
}
How about check if any objects exist and if not, create one with some data?
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Settings"];
_managedObjectSettings = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
if ([_managedObjectSettings count] == 0) {
// first time, create some defaults
NSManagedObject *newDevice = [NSEntityDescription insertNewObjectForEntityForName:#"Settings" inManagedObjectContext:managedObjectContext];
[newDevice setValue:[NSNumber numberWithBool: YES ] forKey:#"speed"];
[newDevice setValue:[NSNumber numberWithBool: YES ] forKey:#"sound"];
[newDevice setValue:[NSNumber numberWithBool: NO ] forKey:#"aspect"];
[newDevice setValue:[NSNumber numberWithBool: NO ] forKey: #"useH264"];
[newDevice setValue:[NSNumber numberWithBool: NO ] forKey: #"useThumbnail"];
NSError *error = nil;
// Save the object to persistent store
if (![managedObjectContext save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
}
Another method for storing defaults is found by way of NSUserDefaults. (surprise!)
And its easy.
Suggested by some, put that into the applicationDidFinishLaunching
In the given case of 10 defaults, Airport0 thru 9
Setting
NSUserDefaults *nud = [NSUserDefaults standardUserDefaults];
[nud setString:#"MACADDRESSORWHY" forKey:#"Airport0"];
...
[nud setString:#"MACADDRESSORWHY" forKey:#"Airport9"];
[nud synchronize];
or
[[NSUserDefaults standardUserDefaults] setString:#"MACADDRESSORWHY" forKey:#"Airport9"]];
...
[[NSUserDefaults standardUserDefaults] synchronize];
And then, getting the defaults.
NSString *air0 = [[NSUserDefaults standardUserDefaults] stringForKey:#"Airport0"];
As most answers are quite old, I recommend the following tutorial. It explains how it can be done.
https://www.youtube.com/watch?v=xcV8Ow9nWFo