Coredata NSpredicate predicate date in swift - 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 []
}
}

Related

Swift 5 NSFetchRequest predicate when trying to lookup a String UUID

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)

Swift sorting Relationship in CoreData

I would love to get help on this one since I've been on it for a while.
So I fetch my Sections like this and they all get in the right order, but the relationship "Todoitem" they have do not come in the same order. They get all in the correct Section but in wrong order.
I wanna get the Todoitems like this:
Section[
Todoitem "first created",
Todoitem "second created",
Todoitem "third created"...]
Instead they come like this when I create them, always random:
Section[
Todoitem "second created",
Todoitem "third created",
Todoitem "first created"...]
You guys in here are awesome and if someone would like to take the time to help me I would be more than tankful!
I'm working in Swift Xcode, Storyboard.
func fetchSections() {
let startOfDay = Calendar.current.startOfDay(for: currentDate)
var components = DateComponents()
components.day = 1
components.second = -1
let endOfDay = Calendar.current.date(byAdding: components, to: startOfDay)
let fetchRequest : NSFetchRequest<Section> = Section.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "date >= %# AND date <= %#", startOfDay as NSDate, endOfDay! as NSDate)
do {
self.tableViewData = try context.fetch(fetchRequest)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
catch {
//error
}
}
Fixed it with this:
extension Section {
var sortedItems: [TodoItem] {
guard let items = todoitem as? Set<TodoItem> else {
return []
}
return items.sorted { (item1, item2) in
return item1.date ?? Date() < item2.date ?? Date()
}
}
}
and in cell for row:
let text = self.tableViewCoreData[indexPath.section].sortedItems[indexPath.row - 1].todo
cell.myLabel.text = text
Predicate is for filtering data only. You probably need to use an NSSortDescriptor to get the output you desire.
After this line:
fetchRequest.predicate = NSPredicate(format: "date >= %# AND date <= %#",
startOfDay as NSDate, endOfDay! as NSDate)
Add these lines:
let dateSortDescriptor = NSSortDescriptor(key: "date", ascending: true)
fetchRequest.sortDescriptors = [dateSortDescriptor]
Update
As Joakim Danielson rightly mentioned in the comments, the sorting cannot be directly applied on sections as the date resides in the ToDoItems entity.
Since you are creating a fetch request using the sections entity, I am not sure you can apply the sort descriptor directly to your to do items entity (atleast to my knowledge).
I would do the following updates to get your desired goal:
First change your var tableViewData to be compatible with this solution
// I am assuming Section and ToDoItems are names of your entities
// and NOT the name of the relationship between them so create this
var tableViewData: [Section: ToDoItem] = [:]
Then make some updates in the fetching
// Since you said this works fine, I do not make any changes to these two lines
let fetchRequest : NSFetchRequest<Section> = Section.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "date >= %# AND date <= %#",
startOfDay as NSDate, endOfDay! as NSDate)
// Temp container to build the desired sorted results
var sectionItems: [Section: [ToDoItems]] = [:]
do {
// Retrieve the sections
let sections = try context.fetch(fetchRequest)
// Loop over all the sections you retrieve
for section in sections
{
// Initialize a sort descriptor for the date attribute in the ToDoItems
// entity. Change it to the right name if it is not date
let sortDescriptor = [NSSortDescriptor(key: "date", ascending: true)]
// Change hasToDoItems to your correct relationship name
if let sortedItems = section.hasToDoItems?
.sortedArray(using: sortDescriptor) as? [ToDoItem]
{
sectionItems[section] = sortedItems
}
}
// Assign the tableViewData with the sorted data
self.tableViewData = sectionItems
// Reload your data
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
Give this a go and let me know if this gives you the desired results or comment with your results and I'll amend the code accordingly.
Fixed it with this:
extension Section {
var sortedItems: [TodoItem] {
guard let items = todoitem as? Set<TodoItem> else {
return []
}
return items.sorted { (item1, item2) in
return item1.date ?? Date() < item2.date ?? Date()
}
}
}
and in cell for row:
let text = self.tableViewCoreData[indexPath.section].sortedItems[indexPath.row - 1].todo
cell.myLabel.text = text
Got help from a friend of mine, thanks anyways!

Core Data. Filter by date

I have a .xcdatamodel in Core Data with the Entity Content and the Attributes dateCreated Type Date and text1 Type String.
//CREATE
func createData() {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let managedContext = appDelegate.persistentContainer.viewContext
let contentEntity = NSEntityDescription.entity(forEntityName: "Content", in: managedContext)!
let content = NSManagedObject(entity: contentEntity, insertInto: managedContext)
content.setValue(Date(), forKey: "dateCreated")
content.setValue("someText", forKey: "text1")
do {
try managedContext.save()
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
}
//READ
func readData() {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let managedContext = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Content")
fetchRequest.predicate = NSPredicate(format: "text1 = %#", "some text")
do {
let result = try managedContext.fetch(fetchRequest)
for data in result as! [NSManagedObject] {
print(data.value(forKey: "dateCreated") as! Date)
print(data.value(forKey: "text1") as! String)
}
} catch {
print("Failed")
}
}
if I print dateCreated, it gives: 2020-03-12 10:20:42 +0000
My question is: how can I read and filter by date? Only date, not time. For instance 2020-03-12 or any other date without time:
fetchRequest.predicate = NSPredicate(format: "dateCreated = %#", ?????)
In CreateData() you're storing the date by using Date(). So that it will be stored in you're database with the format yyyy-MM-dd HH:mm:ss ZZZZ. That's why it prints this 2020-03-12 10:20:42 +0000
Change from
fetchRequest.predicate = NSPredicate(format: "text1 = %#", "some text")
to
fetchRequest.predicate = NSPredicate(format: "dateCreated = %#", Date())
if you using this predicate instead of text1, the core data will give you result with Date and it'll be difficult to filter if there is time. So you have to insert the date by changing its format to yyyy-MM-dd
You can change the current date format by using DateFormatter().
//Needed date format
let dateFormat = DateFormatter()
dateFormat.dateFormat = "yyyy-MM-dd"
let formattedDate = dateFormat.string(from: Date())
So try by inserting the Date() by changing its format
//in CreateData()
content.setValue(formattedDate, forKey: "dateCreated")
//in ReadData()
fetchRequest.predicate = NSPredicate(format: "dateCreated = %#", formattedDate)

Predicate in realm with dictionary parameters

I want to create a predicate with a dictionary paramateres and filter data on realm, like
var parameters = [String: Any]()
parameters["Mobile"] = a.Mobile!
parameters["CategoryId"] = self.SelectCategryId
let existContact = (contactBiz.FetchMulti(parameters: parameters)?.count)! > 0
and in fetchMulti func I make a predicate and filter data
func FetchMulti(parameters: [String: Any])-> Results<RealmEntityType>?
{
do
{
let key = T.KeyName()
var object = realm.objects(RealmEntityType.self)
let subPredicates = parameters.map {
NSPredicate(format: "%# = %#", $0.key, $0.value as! CVarArg)
}
let compoundPredicate = NSCompoundPredicate(orPredicateWithSubpredicates: subPredicates)
// var predictionArray = [String]()
// for p in parameters
// {
// predictionArray.append("\(p.key) = \(p.value) ")
//
// }
//
// let perdicate = predictionArray.joined(separator: " && ")
//
return object.filter(compoundPredicate);
}
catch
{
print(error.localizedDescription)
}
return nil
}
but I get this error
reason: 'Predicate expressions must compare a keypath and another keypath or a constant value'
Any help!
You need to use %K to indicate that the value is the name of a key and not a constant string: NSPredicate(format: "%K = %#", $0.key, $0.value as! CVarArg)

Error handling on fetch request and NSpredicate

I am extracting data from the past week using CoreData. It all works fine for days when some data is stored. However, when no data is stored, the program crashes. How can I handle this error?
for i in 0...6 { // Get every day in the past week
let appDel = (UIApplication.sharedApplication().delegate as! AppDelegate)
let context: NSManagedObjectContext = appDel.managedObjectContext
let request = NSFetchRequest(entityName: "Pulser")
request.returnsObjectsAsFaults = false
let calendar = NSCalendar.currentCalendar()
let now = NSDate()
let beginningOfToday = calendar.startOfDayForDate(now)
let previousDay = calendar.startOfDayForDate(calendar.dateByAddingUnit(.Day, value: -i, toDate: beginningOfToday, options: [])!)
let previousPreiviousDay = calendar.startOfDayForDate(calendar.dateByAddingUnit(.Day, value: -i - 1 , toDate: beginningOfToday, options: [])!)
request.predicate = NSPredicate(format:"(date >= %#) AND (date < %#)", previousPreiviousDay, previousDay)
do {
let results = try context.executeFetchRequest(request) as! [Pulser]
pulseArray.append(meanValue(results)) // if more than one value stored for each day, take the mean value of it
}
catch let error as NSError {
print(error)
}
}
It works like a charm now, thanks.
if results.count > 0 {
pulseArray.append(meanValue(results))
}