Swift 5 NSFetchRequest predicate when trying to lookup a String UUID - swift

I have a string UUID coming into this method, to lookup an entity in CoreData that has UUID's saved as UUID type (Not String).
I keep getting "Fatal error: Unexpectedly found nil while unwrapping an Optional value" on line for the predicate.
func loadUser(uuid: String) -> [ExistingUsers2] {
let request : NSFetchRequest<ExistingUsers2> = ExistingUsers2.fetchRequest()
let uuidQuery = NSUUID(uuidString: uuid)
request.predicate = NSPredicate(format: "%K == %#", #keyPath(ExistingUsers2.uuid), uuidQuery! as CVarArg)
request.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
do {
existingUsersArray = try context.fetch(request)
print("Returned \(existingUsersArray.count)")
} catch {
print("Error fetching data from context \(error)")
}
return existingUsersArray
}
Any help? I haven't found anything here or Dr Google. TKS

You can replace your predicate with this:
guard let uuidQuery = UUID(uuidString: uuid) else { return [] } // no valid UUID with this code
request.predicate = NSPredicate(format: "%K == %#", #keyPath(ExistingUsers2.uuid), uuidQuery as CVarArg)
Everything else should work.
UPDATE
This is the code that finally worked, thanks for your help #André Henrique da Silva
func loadUser(uuid: String) -> [ExistingUsers2] {
let request : NSFetchRequest<ExistingUsers2> = ExistingUsers2.fetchRequest()
let uuidQuery = NSUUID(uuidString: uuid)
request.predicate = NSPredicate(format: "uuid == %#", uuidQuery! as CVarArg)
request.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
do {
existingUsersArray = try context.fetch(request)
} catch {
print("Error fetching data from context \(error)")
}
return existingUsersArray
}

Try this as your predicate: NSPredicate(format: "cid = %#", "\(id)")
where cid is the UUID in CoreData and id is the UUID you got from the string. Also do not use NSUUID.

Replace the uuidAttributeName with your attribute name and yourStringuuid with the your string that you want to convert into UUID type.
var uuid = UUID(uuidString: yourStringuuid)
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "ExistingUsers2")
fetchRequest.predicate = NSPredicate(format: "uuidAttributeName == %#",uuid)
let results = try context.fetch(fetchRequest)

Related

how to modify my Core Data attributes of an entity but on all the elements of my database

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
}

Coredata NSpredicate predicate date in swift

A old classic question but I got.
func callThisDay(startDate:Date, endDate:Date) -> [Login]{
var datas = [Login]()
let fetchRequest:NSFetchRequest<Login> = Login.fetchRequest()
let predicate = NSPredicate(format: "date >= %# AND date < %#", argumentArray: [startDate, endDate])
fetchRequest.predicate = predicate
do{
let allData = try viewContext.fetch(Login.fetchRequest())
for data in allData{
datas.append(data as! Login)
}
}catch{
print(error)
}
return datas
}
And it always return all data. How can I just got a day?
Of course it always returns all data because you are ignoring the custom fetch request.
Replace
let allData = try viewContext.fetch(Login.fetchRequest())
with
let allData = try viewContext.fetch(fetchRequest)
The method can be simplified
func callThisDay(startDate:Date, endDate:Date) -> [Login] {
let fetchRequest : NSFetchRequest<Login> = Login.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "date >= %# AND date < %#", startDate as NSDate, endDate as NSDate)
do {
return try viewContext.fetch(fetchRequest)
} catch {
print(error)
return []
}
}

Swift Core Data fetch data 1 column [duplicate]

This question already has an answer here:
Fetching selected attribute in entities
(1 answer)
Closed 4 years ago.
I cannot fetch data by "name" column. When respone, system print all attribute in People. Help me, thanks.
private func getPeople(product: String) {
let temp = product
let entityDescription = NSEntityDescription.entity(forEntityName: "People", in: context)
let fetchRequest = NSFetchRequest<NSFetchRequestResult>()
fetchRequest.entity = entityDescription
fetchRequest.includesPropertyValues = true
fetchRequest.returnsObjectsAsFaults = false
fetchRequest.predicate = NSPredicate(format: "product == %#", temp)
fetchRequest.propertiesToFetch = ["name"]
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "id", ascending: true)]
do {
let personList = try context.fetch(fetchRequest) as! [People]
print(personList)
} catch let error as NSError {
print(error)
}
}
To fetch only specific properties the resultType of the request must be dictionaryResultType
private func getPeople(product: String) {
let fetchRequest = NSFetchRequest<NSDictionary>(entityName: "People")
fetchRequest.predicate = NSPredicate(format: "product == %#", product)
fetchRequest.resultType = .dictionaryResultType
fetchRequest.propertiesToFetch = ["name"]
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "id", ascending: true)]
do {
let personList = try context.fetch(fetchRequest) as! [[String:String]]
print(personList)
} catch {
print(error)
}
}
}

What is predicate to use that contains a word

Which predicate i need to use where objects in returned from Core Data array:
First objects must match completely;
Other object must just contained the specific word;
For example:
I have entity Man(firstName:String, lastName: String).
Let's say, i have this objects in Core Data:
1) Man(firstName: "John", secondName: "Alexandrov"), 2) Man(firstName: "Alex", secondName: "Kombarov"), 3) Man(firstName: "Felps", secondName: "Alexan").
And in returned arr i want to see [Man(firstName: "Alex", secondName: "Kombarov"), Man(firstName: "Felps", secondName: "Alexan"), Man(firstName: "John", secondName: "Alexandrov")]
How can i achieved this?
You could use a NSCompoundPredicate.
First, you'd create a predicate for the firstName. This one would be strict, so you'd search for matches using ==:
let firstNamePredicate = NSPredicate(format: "%K == %#", argumentArray: [#keyPath(Man.firstName), "alex"])
Then, you'd create a predicate for the lastName. This one is less strict, so you'd use CONTAINS:
let lastNamePredicate = NSPredicate(format: "%K CONTAINS[c] %#", argumentArray: [#keyPath(Man.lastName), "alex"])
Then you'd create an NSCompoundPredicate using the orPredicateWithSubpredicates signature.
let compoundPredicate = NSCompoundPredicate(orPredicateWithSubpredicates: [firstNamePredicate, lastNamePredicate])
From there, you could create a NSFetchRequest and assign compoundPredicate as the predicate for the fetchRequest.
If you want to sort the results, you can add one or more NSSortDescriptors to your NSFetchRequest:
let sortByLastName = NSSortDescriptor(key: #keyPath(Man.lastName), ascending: true)
let sortByFirstName = NSSortDescriptor(key: #keyPath(Man.firstName), ascending: true)
request.sortDescriptors = [sortByLastName, sortByFirstName]
Then, you'd do the fetch:
let request: NSFetchRequest = Man.fetchRequest()
request.predicate = compoundPredicate
var results: [Man] = []
do {
results = try context.fetch(request)
} catch {
print("Something went horribly wrong!")
}
Here's a link to a useful post on NSPredicate
Adding to #Adrian answer, I had to make couple changes for it to work.
let FIRSTNAME = "Alex"
let LASTNAME = "Smith"
let firstNamePredicate = NSPredicate(format: "firstName == %#", FIRSTNAME)
let lastNamePredicate = NSPredicate(format: "firstName == %#", LASTNAME)
let compoundPredicate = NSCompoundPredicate(orPredicateWithSubpredicates: [firstNamePredicate, lastNamePredicate])
request.predicate = compoundPredicate
do {
results = try context.fetch(request)
} catch {
print("Something went horribly wrong!")
}

Swift NSPREDICATE not like value

EntityA: Gymnast
firstName
lastName
one to many MeetResults
EntityB: MeetResults
meetDate
barScore
beamScore
floorScore
vaultScore
I am loading all gymnasts who are set as active into the picker. As I create an NSSET result.meetresults = meetScore.copy() as? NSSet which works fine I am trying to figure out how to remove a gymnast who already has a NSSET for this date. Any assistance would be appreciated.
func getGymnasts() {
let fetchRequest = NSFetchRequest(entityName: "Gymnast")
let sortDescriptor1 = NSSortDescriptor(key: "fullName", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor1]
let filter1 = NSPredicate(format: "isActive == %#", "Yes")
let filter2 = NSPredicate(format: "ANY meetresults.meetDate != %#", "\(meetDateText.text!)")
let predicate = NSCompoundPredicate(type: NSCompoundPredicateType.AndPredicateType, subpredicates: [filter1, filter2])
fetchRequest.predicate = predicate
//fetchRequest.predicate = filter1
do {
self.gymnastArray = try AD.managedObjectContext.executeFetchRequest(fetchRequest) as! [Gymnast]
self.gymnastPicker.reloadAllComponents()
} catch {
fatalError("Fetch Failed")
}
}
To-many relationships are awkward sometimes. You can use the subquery syntax to query the count of an internal fetch to find Meets matching your criteria, then check there aren't any:
let filter2 = NSPredicate(format: "SUBQUERY(meetresults.meetDate, $m, $m.meetDate == %#).#count == 0", meetDateText.text!)