I have a question, I would like to test is my managedObject is empty then generate my CoreData.
let fetchRequest = NSFetchRequest(entityName: "Adresses")
let results = try managedContext.executeFetchRequest(fetchRequest)
AdressesProjecteurs = results as! [NSManagedObject]
print(AdressesProjecteurs)
if (AdressesProjecteurs.managedContextTmp == nil) {
saveName()
}
My if condition is generating a error, so, how to test if my AdressesProjecteurs is empty ?
Thank you
just don't save empty objects and then check if there IS an object saved after all
//check if it was NEVER saved
let fetchRequest = NSFetchRequest(entityName: "Adresses")
let results = try managedContext.executeFetchRequest(fetchRequest)
AdressesProjecteurs = results as! [NSManagedObject]
print(AdressesProjecteurs)
if (AdressesProjecteurs.count == 0) {
saveName()
}
Related
hello community I am a novice and this is my first question.
how to change all the attributes of an entity and be able to change all my Core Data elements,
because I can only change the first attribute of an entity but not all my data records.
Here in this function I can only change the name
and then I get this following error has the line:
let objectUpdate = test[0] : Thread 1: Fatal error: Index out of range
func updateData() {
var newName = ""
var newPrenom = ""
newName = name.text!
newPrenom = prenom.text!
let managedContext = AppDelegate.viewContext
let fetchRequest : NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "Person")
fetchRequest.predicate = NSPredicate(format: "name = %#", newName)
do {
fetchRequest.predicate = NSPredicate(format: "prenom = %#", newPrenom)
let test = try! managedContext.fetch(fetchRequest) as! [NSManagedObject]
let objectUpdate = test[0]
objectUpdate.setValue(newName,forKey: "name")
objectUpdate.setValue(newPrenom, forKey: "prenom")
do {
try managedContext.save()
}
catch {
print(error)
}
} catch {
print(error)
}
}
There are a number of ways we can avoid this error.
Unwrapping optional .first value
Swift's Collection gives us safe way to get first item, simply by accessing the first property on a given collection. It will return an Optional<Element> value so we need to unwrap it first either by using if let of guard let
if let object = test.first {
// do something with object
}
or
guard let object = test.first else { return }
// do something with object
Checking if value at index exists
It's often a good idea to check for a specific index within the indices property before accessing the value behind it.
if test.indices.contains(0) {
let object = test[0]
// do something with object
}
These hints should prevent your code from crashing again.
Other Suggestions
This is not really safe or clean:
var newName = ""
var newPrenom = ""
newName = name.text!
newPrenom = prenom.text!
We can make it much cleaner and most importantly safer by using a guard statement
guard let newName = name.text, let newPrenom = prenom.text else { return }
Two important things happened here:
No more force-unwrapping the optional values of text [which could cause a crash]
The properties are now immutable, meaning we can be sure that what we are saving to the CoreDate is what was retreived at the beginning of the function
Since the line:
let test = try! managedContext.fetch(fetchRequest) as! [NSManagedObject]
is already wrapped in the do-catch clause, you can safely remove forced try! and replace it with try.
let test = try managedContext.fetch(fetchRequest) as! [NSManagedObject]
Let's use types! On this line you create a NSFetchRequest object for some entity named "Person".
let fetchRequest : NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "Person")
I am guessing CoreData have generated for you a NSManagedObject subclass, named Person. If this is true, you could rewrite it like this:
let fetchRequest = NSFetchRequest<Person>(entityName: "Person")
With the previous tip implemented, we can now get rid of as! [NSManagedObject] from this line:
let test = try managedContext.fetch(fetchRequest) as! [NSManagedObject]
Since the NSFetchRequest object is now nicely typed, we can take advantage of it by rewriting it like this:
let test: [Person] = try managedContext.fetch(fetchRequest)
So we are using proper types now? cool! Lets now improve this:
objectUpdate.setValue(newName,forKey: "name")
objectUpdate.setValue(newPrenom, forKey: "prenom")
by rewriting this and using properties on Person object
objectUpdate.name = newName
objectUpdate.prenom = newPrenom
No need for introducing second level of do-catch clause, since we are already in one!
do {
try managedContext.save()
}
catch {
print(error)
}
you can easily replace it with just the save() call, like this:
try managedContext.save()
Are you sure these predicates are what you want?
fetchRequest.predicate = NSPredicate(format: "name = %#", newName)
fetchRequest.predicate = NSPredicate(format: "prenom = %#", newPrenom)
What I can read from them is that you are fetching Person object where the name is newName and prenom is newPrenom and then you update it with the same exact values? Are you using some kind of identification of users? like id: Int or id: UUID? It would make much more sense to write something like this
let id: Int = // ID of the user you are currently editing
fetchRequest.predicate = NSPredicate(format: "id == \(id)")
if you are not using any id's, you could try storing the initial values of name and prenom
// in cell declaration - set when you configure your cell
var initialName: String?
var initialPrenom: String?
// then in your function:
fetchRequest.predicate = NSPredicate(format: "name = %#", initialName)
fetchRequest.predicate = NSPredicate(format: "prenom = %#", initialPrenom)
But I just noticed you also override you first predicate with the second one. You need to use NSCompoundPredicate
fetchRequest.predicate = NSCompoundPredicate(
type: .and, subpredicates: [
NSPredicate(format: "name = %#", initialName),
NSPredicate(format: "prenom = %#", initialPrenom)
]
)
Suggested version
func updateData() {
guard let newName = name.text, let newPrenom = prenom.text else { return }
let managedContext = AppDelegate.viewContext
let fetchRequest = NSFetchRequest<Person>(entityName: "Person")
fetchRequest.predicate = NSCompoundPredicate(
type: .and, subpredicates: [
NSPredicate(format: "name = %#", initialName),
NSPredicate(format: "prenom = %#", initialPrenom)
]
)
do {
let objects: [Person] = try managedContext.fetch(fetchRequest)
guard let object = objects.first else { return }
object.name = newName
object.prenom = newPrenom
try managedContext.save()
} catch {
print(error)
}
}
If the index 0 is out of range, it means that the array is empty. Before accessing it, add
if test.isEmpty{
return //the fetch request didn't return any values
}
I'm trying to add a filter (boolean value) to a fetch in core data, but I'm not sure how.
The entity name is Vinyl and the attribute that I want to use to filter is called wishlist (true or false)
if let context = (UIApplication.shared.delegate as? AppDelegate)?.persistentContainer.viewContext {
if let vinyls = try? context.fetch(Vinyl.fetchRequest()) {
if let theVinyls = vinyls as? [Vinyl] {
self.allVinyls = theVinyls
totalVinyls = theVinyls.count
}
}
}
How can I filter for wishlist == true ?
Add an appropriate predicate.
All if lets are not necessary, the viewContext is supposed to be non-optional.
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let request : NSFetchRequest<Vinyl> = Vinyl.fetchRequest()
let predicate = NSPredicate(format: "wishlist == TRUE")
request.predicate = predicate
do {
self.allVinyls = try context.fetch(request)
totalVinyls = self.allVinyls.count
} catch { print(error) }
I'm going crazy trying to retrieve a fact for a given question in Core data. The code I'm using is below and what I'd like to do is let the user select a "question" and see the related "facts" for it.
I'm going around in circles trying to do this. Can anyone explain to me what I'm missing? Thank you
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let managedContext = appDelegate.persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "BadHusband")
request.predicate = NSPredicate(format: "question= %#", itemSelected)
request.returnsObjectsAsFaults = false
do {
let results = try managedContext.fetch(request)
stuff = results as! [NSManagedObject]
if results.count > 0 {
for result in results as! [NSManagedObject] {
if let question = result.value(forKey: "question"), let facts = result.value(forKey: "facts") {
print(question)
print(facts)
}
}
}
I'm trying to put the results of a fetch request into an array. My code:
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "CLIENTS")
var mobClients = [NSManagedObject]()
var arrayAllPhoneNumbers = [String]()
do {
let results = try managedContext.executeFetchRequest(fetchRequest)
mobClients = results as! [NSManagedObject]
for clientPhoneNumber in mobClients {
let myClientPhoneNumber = clientPhoneNumber.valueForKey("clientsMobilePhoneNumber") as! String
print(myClientPhoneNumber)
//The numbers print out just fine, one below the other
//
//Now the results need to go into the array I've declared above ---> arrayAllPhoneNumbers
messageVC.recipients = arrayAllPhoneNumbers // Optionally add some tel numbers
}
} catch
let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
As illustrated, all the phone numbers needs to be captured in an array. How do I accomplish that?
Instead of your for-loop and the code inside it, use this:
arrayAllPhoneNumbers = mobClients.map({ clientPhoneNumber in
clientPhoneNumber.valueForKey("clientsMobilePhoneNumber") as! String
})
messageVC.recipients = arrayAllPhoneNumbers
let request = NSFetchRequest(entityName: "CLIENTS")
let results = (try? managedContext.executeFetchRequest(request)) as? [NSManagedObject] ?? []
let numbers = results.flatMap { $0.valueForKey("clientsMobilePhoneNumber" as? String }
numbers is now an array of your phone numbers.
But like thefredelement said, it's better to subclass it so you can just cast it to that subclass and access the phone numbers directly.
Swift 5 : flatMap is deprecated, use compactMap
let request = NSFetchRequest(entityName: "CLIENTS")
let results = (try? managedContext.executeFetchRequest(request)) as? [NSManagedObject] ?? []
let numbers = compactMap { $0.valueForKey("clientsMobilePhoneNumber" as? String }
I'm using the following code to fetch all the data in "category".
let appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let managedContext = appDelegate.managedObjectContext!
let fetchRequest = NSFetchRequest(entityName:"category")
let fetchedResults = managedContext.executeFetchRequest(fetchRequest, error: &error) as [NSManagedObject]?
How do I only brings categories where the "type" is equal to "products"?
To "filter" results in Core Data, use NSPredicate like so:
let filter = "products"
let predicate = NSPredicate(format: "type = %#", filter)
fetchRequest.predicate = predicate
So, in the below example
In the first step, we will add give the value we want to filter.
In the second step, we will add a predicate in which we will give the DB key we want to get the value in my case is "id" and besides this a value, we want to filter.
In the Third step assign the entity name where your all data has saved in the NSFetchRequest.
After that assign the predicate.
Use context object to fetch the object.
private let context = (NSApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let filter = id
let predicate = NSPredicate(format: "id = %#", filter)
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName:"TodoListItem")
fetchRequest.predicate = predicate
do{
let fetchedResults = try context.fetch(fetchRequest) as! [NSManagedObject]
print("Fetch results")
if let task = fetchedResults.first as? TodoListItem{
print(task)
}
// try context.save()
}catch let err{
print("Error in updating",err)
}
You can use .filter on the fetchedResults.