I have a problem where i cannot completely delete data stored in core data entities. i am storing the username and password in a login system.
i am aware of using the .deleteObject() method but to be honest im not sure if this is actually working as i do a print statement to check in console what is stored and it appears to have not been deleted
here is my code:
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let context: NSManagedObjectContext = appDelegate.managedObjectContext
//add user to core data
let user = NSEntityDescription.insertNewObjectForEntityForName("User", inManagedObjectContext: context)
do{
try context.save()
}catch{
print("there was an error saving to core data")
}
do{
let request = NSFetchRequest(entityName: "User")
let results = try context.executeFetchRequest(request)
if results.count > 0{
for item in results as! [NSManagedObject]{
let username = item.valueForKey("username")
let password = item.valueForKey("password")
context.deleteObject(item)
print("aawawaw \(username), \(password)")
}
}
}catch{
print("there was an error")
}
You are printing the values that are stored in the let constants .
Try to implement in for loop
context.deleteObject(item)
let username = item.valueForKey("username")
let password = item.valueForKey("password")
print("aawawaw \(username), \(password)")
You did not really delete the objects from CD.
You should:
delete the object.
store the context
then perform a new fetch on the entity
print all instances of the entity
What you actually do is different:
store the context (with which changes whatever, but noting related to your question)
fetch the entity
delete object from the CD context
print the object that is still around although detached from CD.
as far as t. You are doing:
we can see, you don't save anything and threfore don't save the deleted state.
Related
I have close to 7K items stored in a relation called Verse. I have another relation called Translation that needs to load 7K related items with a single call from a JSON file.
Here is my code:
let container = getContainer()
container.performBackgroundTask() { (context) in
autoreleasepool {
for row in translations{
let t = Translation(context: context)
t.text = (row["text"]! as? String)!
t.lang = (row["lang"]! as? String)!
t.contentType = "Verse"
t.verse = VerseDao.findById(row["verse_id"] as! Int16, context: context)
// this needs to make a call to the database to retrieve the approparite Verse instance.
}
}
do {
try context.save()
} catch {
fatalError("Failure to save context: \(error)")
}
context.reset()
}
Code for the findById method.
static func findById(_ id: Int16, context: NSManagedObjectContext) -> Verse{
let fetchRequest: NSFetchRequest<Verse>
fetchRequest = Verse.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "verseId == %#", id)
fetchRequest.includesPropertyValues = false
fetchRequest.fetchLimit = 1
do {
let results =
try context.fetch(fetchRequest)
return results[0]
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
return Verse()
}
}
This works fine until I add the VerseDao.findById, which makes the whole process really slow because it has to make a request for each object to the Coredata database.
I did everything I could by limiting the number of fetched properties and using NSFetchedResultsController for data fetching but no luck.
I wonder if there's any way to insert child records in a more efficient way? Thanks.
Assuming your store type is persistent store type is sqlite (NSSQLiteStoreType):
The first thing you should check is whether you have an Core Data fetch index on the Verse objects verseId property. See this stack overflow answer for some introductory links on fetch indexes.
Without that, the fetch in your VerseDao.findById function may be scanning the whole database table every time.
To see if your index is working properly you may inspect the SQL queries generated by adding -com.apple.CoreData.SQLDebug 1 to the launch arguments in your Xcode scheme.
Other improvements:
Use NSManagedObjectContext.fetch or NSFetchRequest.execute (equivalent) instead of NSFetchedResultsController. The NSFetchedResultsController is typically used to bind results to a UI. In this case using it just adds overhead.
Don't set fetchRequest.propertiesToFetch, instead set fetchRequest.includesPropertyValues = false. This will avoid fetching the Verse object property values which you don't need to establish the relation to the Translation object.
Don't specify a sortDescriptor on the fetch request, this just complicates the query
I'm trying to save strings I have in my string array into the Core Data. My .xcdatamodel looks like this:
My saving function (a method of a class called "Memory"):
func save(from: [String])
{
for i in 0..<from.count
{
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let saved = NSEntityDescription.insertNewObject(forEntityName: "Person", into: context)
saved.setValue(from[i], forKey: "password")
do
{
try context.save()
print("SAVED")
}
catch
{
print("ERROR - COULDN'T SAVE ", to)
}
}
print("NEW ", to, ": ")
print(save)
}
Lastly, inside my ViewController:
Memory().save(from: codes)
However, what I get is this:
Thread 1: Fatal error: Unresolved error Error
Domain=NSCocoaErrorDomain Code=134140 "Persistent store migration
failed, missing mapping model."
UserInfo={sourceModel=() isEditable
1, entities {
Person = "() name Person,
managedObjectClassName NSManagedObject, renamingIdentifier Person,
isAbstract 0, superentity name (null), properties {\n password =
\"(), name password, isOptional
1, isTransient 0, entity
You have made changes to your data model but you failed/forgot to migrate the model. If you don't have anything valuable in your current persistence store (SQLite database?) then I suggest you throw it away and let Core Data create a new one using the new model.
Otherwise you might want to fetch the previous version of your model from your source repository if you have one and do a proper migration. Here is a SO question of interest and Apple's documentation on migration
I am trying to use a for loop to create objects, which have attributes I will use later to populate a map. I am able to loop through and populate the attributes of the object, but when I save the object, it seems to be overwriting each time. the for loop pulls data out of my firebase database.
let holes = [hole1,hole2,hole3,hole4,hole5,hole6,hole7,hole8,hole9,hole10,hole11,hole12,hole13,hole14,hole15,hole16,hole17,hole18]
for hole in holes {
let holeX = hole?["HoleX"] as? Double // these are just grabbing the data for the appropriate hole from my firebase data.
let holeY = hole?["HoleY"] as? Double
let holeH = hole?["HoleH"] as? Double
var newHole = NSEntityDescription.insertNewObject(forEntityName: "HoleCoordinateData", into: self.managedObjectContext)
newHole.setValue(holeX, forKey: "HoleX")
newHole.setValue(holeY, forKey: "HoleY")
newHole.setValue(holeH, forKey: "HoleH")
newHole.setValue(holeNumber, forKey: "holeNumber")
newHole.setValue("\(holeNumber)", forKey: "holeNumberString")
self.managedObjectContext.insert(newHole)
holeNumber += 1
}
do {
try self.managedObjectContext.save()
} catch {
print("Could not save Data: \(error.localizedDescription)")
}
I have tried the do try catch within the for loop. I originally had the newHole being initialized prior to the for loop, but I put it inside on the advice of some other stack questions and responses. I originally filled out newHole by just doing newHole.holeX = holeX. obviously some of these changes were just different ways of doing the same thing, but I've been spinning my wheels on this for long enough to try anything I could.
I'm working on a word game and have bundled a complete list of english words using a sqlite database. I'm trying to find the best way to search the database for a given string to determine if it's a word.
At this point I can get the whole database out into an array:
func fetchWords() {
if let managedObjectContext = (UIApplication.shared.delegate as? AppDelegate)?.managedObjectContext {
let wordsFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "EnglishWord")
do {
englishWords = try managedObjectContext.fetch(wordsFetch) as! [EnglishWord]
print(englishWords[123323].word)
//Prints "injustices"
} catch {
//error handling
}
}
}
Now. What I really want to do is pass in a given string and see if it exists as a word in my database. I have a clunky solution with predicates, e.g:
func fetchWordsToArray() {
if let managedObjectContext = (UIApplication.shared.delegate as? AppDelegate)?.managedObjectContext {
let wordsFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "EnglishWord")
let searchWord = ["dastardly"]
let searchPredicate = NSPredicate(format: "word = %#", argumentArray: searchWord)
do {
englishWords = try managedObjectContext.fetch(wordsFetch) as! [EnglishWord]
let resultFilteredByPredicate = (englishWords as NSArray).filtered(using: predicate)
print(resultFilteredByPredicate)
} catch {
//error handling
}
}
}
But in order to use the filtered function I have to convert to an NSArray which means I can't work with the results directly (e.g. get resultFilteredByPredicate.word).
Also it feels like I'm probably going about this all the wrong way. Since everything has to go into an array first, I must be losing a lot of the value of using an sqlite database in the first place.
Any suggested approaches for better working with the database?
Many thanks in advance for any help!
To make the database do the filtering (which could then be optimized automatically with an index), put a predicate on the original fetch request:
let formatRequest : NSFetchRequest<Word> = Word.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "word == %#", searchWord)
let fetchedResults = try context.fetch(fetchRequest) as! [Word]
But Core Data is a framework to manage your objects.
If you decide that your words are not objects but just values, you could replace Core Data with some other library that does SQL directly, and execute an SQL query instead:
SELECT * FROM EnglishWord WHERE word = ?
I'm trying to save a CKRecord (already created and saved to the database) and a new CKShare to it but I keep getting this error: Optional("Failed to modify some records"). Here is my code:
let csc = UICloudSharingController { controller, preparationCompletionHandler in
let share = CKShare(rootRecord: user)
share[CKShareTitleKey] = "My Share" as CKRecordValue
share.publicPermission = .readWrite
let mro = CKModifyRecordsOperation(recordsToSave: [user, share], recordIDsToDelete: nil)
mro.timeoutIntervalForRequest = 10
mro.timeoutIntervalForResource = 10
mro.modifyRecordsCompletionBlock = { records, recordIDs, error in
if error != nil {
print("ERROR IN MODIFY RECORDS COMPLETION BLOCK\n")
print(error?.localizedDescription)
}
preparationCompletionHandler(share,CKContainer.default(), error)
}
privateData.add(mro)
}
csc.availablePermissions = [.allowPrivate,.allowReadWrite]
self.present(csc, animated:true)
}
The problem is in this method: modifyRecordsCompletionBlock. Can somebody explain me why this is happening?
Thanks in advance!
I got it
All you have to do is create a private custom CKZone and save your CKRecord and CKShare in it, you cannot use the default zone cloud kit gives to you!
To leave a shared record, you have to delete the CKShare record from the shared database otherwise you'll get errors.