Filter Array by "isComplete" - ios

I have two data sets "ProjectItem" and "TaskItem", and a project can have many tasks. I want to filter tasks by "isComplete" in the project they belong to.
In my ProjectItem+CoreDataProperties file I have the following:
extension ProjectItem {
#NSManaged public var projectColor: String
#NSManaged public var projectId: UUID
#NSManaged public var projectTitle: String
#NSManaged public var projectDateCreated: Date
#NSManaged public var isFavorite: Bool
#NSManaged public var task: NSSet
public var taskArray: [TaskItem] {
let set = task as? Set<TaskItem> ?? []
-- How can I filter for "isComplete" here? --
}
}
And TaskItem+CoreDataProperties looks like this:
extension TaskItem {
#NSManaged public var completedDate: Date
#NSManaged public var completeIcon: String
#NSManaged public var createdDate: Date
#NSManaged public var dueDate: Date
#NSManaged public var id: UUID
#NSManaged public var isComplete: Bool
#NSManaged public var notes: String
#NSManaged public var priority: String
#NSManaged public var title: String
#NSManaged public var project: ProjectItem?
}
How can I modify the array in the first code snippet to show only tasks where "isComplete" = true?
Many thanks!

You don't need to define the Coredata managed property with dynamic NSSet any more. You could as well use, generic set with the Element type and Core data is able to infer the type from underlying store. So, your class could be changed to something like this,
extension ProjectItem {
#NSManaged public var projectColor: String
#NSManaged public var projectId: UUID
#NSManaged public var projectTitle: String
#NSManaged public var projectDateCreated: Date
#NSManaged public var isFavorite: Bool
// Notice this
#NSManaged public var task: Set<TaskItem>
}
So, for completed task items, you can simply use filter on Set if you want.
extension ProjectItem {
var completedItems: Set<TaskItem> {
return task.filter(\.isComplete) // for 5.2 and above
}
}

It is more optimal to create explicit fetch request and allow CoreData to filter isCompleted tasks by predicate, like
struct TaskView: View {
var tasksRequest : FetchRequest<TaskItem>
var tasks : FetchedResults<TaskItem>{tasksRequest.wrappedValue}
init(){
self.tasksRequest = FetchRequest(entity: TaskItem.entity(), sortDescriptors: [],
predicate: NSPredicate(format: "isComplete == YES"))
}
// ... other code

Related

How to set default value in core data array ? How to override the default value ? How to customize category?

I'm trying to make a Storage Expiration Notification APP.
I create a class called Product, here are the properties.
#NSManaged public var productName: String?
#NSManaged public var quantity: String?
#NSManaged public var category = ["", "Food", "Daily", "Makeup"]
#NSManaged public var chooseCategory: Int16
#NSManaged public var purchaseDate: String?
#NSManaged public var expiredDate: String?
#NSManaged public var productID: String?
But there's an Error showed that #NSManaged property cannot have an initial value
Hence, I only can move the Category array(use picker controller to choose the values) to ViewContorller.swift. But I want to create a Customize Category array that users can change the value. For instance, the default category is ["Food", "Daily", "Makeup"], users can change the value to ["Drink", "Wine", "Battery"]. Should I use archiving, or create a new class? I have no idea how to implement it.
The Core Data way to do this is by overriding awakeFromInsert. That function is inherited from NSManagedObject and is called once when the object is first inserted into a managed object context. For this case it would look something like
func awakeFromInsert() {
category = ["", "Food", "Daily", "Makeup"]
}
It doesn't work in exactly the same way as a Swift initial value but it has the same effect since it happens when you create a new instance.
The error is right, an #NSManaged property cannot have an initial value.
A lightweight Swift solution is a JSON string attribute and a computed property for the conversion.
#NSManaged public var category : String
and
var categoryArray : [String] {
get { (try? JSONDecoder().decode([String].self, from: Data(category.utf8))) ?? [] }
set {
let data = try! JSONEncoder().encode(newValue)
category = String(data: data, encoding: .utf8)!
}
}
Set the default value
"[\"\",\"Food\",\"Daily\",\"Makeup\"]
either in awakeFromInsert or in Interface Builder in the model

Filter BehaviorRelay array rxswift

I have a BehaviorRelay with an Array of FamilyTaskCoreData inside it. In FamilyTaskCoreData I have the "owner" parameter and I want to filter the Array where it has the id of "45332523dqwd" or an other query.
This is my BehaviorRelay:
private var familyTask = BehaviorRelay<[FamilyTasksCoreData]>(value: [])
And this is the code I use to bind it:
let item = memberData.getTaskData(memberID: queryID)
item
.filter(
$0.filter{ $0.name.hasPrefix("M")}
)
.bind(to: tableView.rx.items(cellIdentifier: "familyCleaningPlanCell", cellType: FamilyCleaningPlanTableViewCell.self)) {[weak self] (row, element, cell) in
cell.titleLabel.text = element.title
cell.checkMarcButton.isSelected = element.status
cell.categoryImage.image = self?.defineImage(name: element.category ?? "")
self?.updateAnItem(cell: cell, data: element)
}.addDisposableTo(disposeBag)
}
I tried to filter it with the filter statement...because I saw it on another question, but I can't find something after $0.. in my case there is no value which I can select.
FamilyTasksCoreData:
#NSManaged public var category: String?
#NSManaged public var end: Date
#NSManaged public var id: String?
#NSManaged public var start: Date
#NSManaged public var status: Bool
#NSManaged public var title: String?
#NSManaged public var createdAt: Date
#NSManaged public var owner: String?
#NSManaged public var familyID: String?
If I understand correctly, you want the tableView to only show FamilyTasksCoreData objects that have a name beginning with "M".
To do that, you need to use a map on the relay instead of a filter. If you use a filter on the relay, you will filter out the entire array all at once, instead of the individual elements.
Instead, you want to use map to transform each array you get from the relay, filtering out the elements that do not begin with "M".
Your code should look something like this:
item
.map {
$0.filter { $0.name.hasPrefix("M") }
}
.bind(...
You need to use single filter to filter your array
item.filter{ $0.owner.hasPrefix("M")}

Usage of LazyMapSequence when an array of Strings is excepted

I have a User object
#objc(User)
public class User: NSManagedObject {
#NSManaged public var firstname: String
#NSManaged public var lastname: String
#NSManaged public var country: String
#NSManaged public var friends: NSSet // of User objects
var full: String {
firstname + " " + lastname
}
var friendsArray: [User] {
friends.allObjects as? [User] ?? []
}
}
and at some point I want to map a large array of users (80k objects) to an array of View models
struct ItemViewModel: Hashable {
let id: UUID
let friendsName: [String]
}
Without lazy it takes a long time, so I have opted for the usag of lazy:
func prepareViewModel(users: [User]) -> [ItemViewModel] {
users.map { user in
let friendsName = user.friendsArray.lazy.filter{["en", "fr"].contains($0.country)}.map(\.full)
return ItemViewModel(id: UUID(), friendsName: friendsName)
}
}
But I get an error:
Cannot convert value of type 'LazyMapSequence<LazyFilterSequence<LazySequence<[User]>.Elements>.Elements, String>'
(aka 'LazyMapSequence<LazyFilterSequence<Array<User>>, String>') to expected argument type '[String]'
It makes sense because now the friends names array will be processed lazily later. I have tried to convert the view model struct to hold:
struct ItemViewModel: Hashable {
let id: UUID
let friendsName: LazyMapSequence<LazyFilterSequence<[User]>, String>
}
But now it's not Hashable is there a way to keep the auto-conformance to Hashable when using LazyMapSequence<LazyFilterSequence<[User]>, String> as type for ItemViewModel and any tips on how to improve performance of logic

is there a function combin NSSet and FetchResult<>

I am coding in Coredata , There are two entity(Message and Keyword) with an one to many relationship.
first , I creat three Keyword instance using #Environment(.managedObjectContext) var moc.
sec, I creat one Message instance using #Environment(.managedObjectContext) var moc
third, I want add three Keywords into the message but faild, the keywords in message is NSSet,
the message is FetchResults, how it can work, Ths.
extension Message {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Message> {
return NSFetchRequest<Message>(entityName: "Message")
}
#NSManaged public var id: UUID?
#NSManaged public var content: String?
#NSManaged public var creatAt: Date?
#NSManaged public var user: User?
#NSManaged public var keywords: NSSet?
#NSManaged public var photoes: NSSet?
}
extension Keyword {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Keyword> {
return NSFetchRequest<Keyword>(entityName: "Keyword")
}
#NSManaged public var id: UUID?
#NSManaged public var title: String?
#NSManaged public var message: Message?
}
.navigationBarTitle("Send Message", displayMode: .inline)
.navigationBarItems(trailing: Button(action: {
}) {
Text("Send")
.onTapGesture {
let newMessage = Message(context: self.moc)
newMessage.keywords = self.keywords
})
}
Your Message class should have a generated method named addToKeywords that takes a Set as an argument
newMessage.addToKeywords(self.keywords)

swift core data relationship one to many for own entity

Hi I'm building a newspaper database which have item relationship to itself (item can have many other item as child)
here is my NSmanageObjectSubclass
import Foundation
import CoreData
extension Item {
#NSManaged var by: String?
#NSManaged var dead: NSNumber?
#NSManaged var descendants: NSNumber?
#NSManaged var id: NSNumber?
#NSManaged var isdeleted: NSNumber?
#NSManaged var score: NSNumber?
#NSManaged var text: String?
#NSManaged var time: NSDate?
#NSManaged var title: String?
#NSManaged var type: String?
#NSManaged var url: String?
#NSManaged var kids: NSSet?
#NSManaged var parent: Item?
#NSManaged var parts: NSSet?
}
Problem is that how can i add item to property kids: NSSet
the relationship define is one to many to its own with reverse. Any help is much appreciate. Thanks.
my coredata xcmodel
First update your class Managed Object class to use Sets instead of NSSet
import Foundation
import CoreData
extension Item {
#NSManaged var by: String?
#NSManaged var dead: NSNumber?
#NSManaged var descendants: NSNumber?
#NSManaged var id: NSNumber?
#NSManaged var isdeleted: NSNumber?
#NSManaged var score: NSNumber?
#NSManaged var text: String?
#NSManaged var time: NSDate?
#NSManaged var title: String?
#NSManaged var type: String?
#NSManaged var url: String?
#NSManaged var kids: Set<Item>
#NSManaged var parent: Item?
#NSManaged var parts: NSSet?
}
Secondly create a fetchRequest named getItemByID with expression
id == $ID
Third create a function to get item by id
func getItemById(ID:String) -> Item?
{
let fetchRequest : NSFetchRequest = self.managedObjectModel.fetchRequestFromTemplateWithName("getItemByID", substitutionVariables: ["ID": ID])!
do {
let fetchedResult = try managedObjectContext!.executeFetchRequest(fetchRequest) as! [Item]
if fetchedResult.count > 0 {
let result = fetchedResult[0]
return result
}
}
catch {
print("Could not fetch \(error)")
}
return nil
}
After that you have many options to set relations
a) you create relation after adding parent & child to coreData
func addChildToParent(childID:String,parentID:String)
{
if let childItem = getItemById(childID)
{
if let parentItem = getItemById(parentID)
{
parentItem.kids.insert(menuItemCatering)
childItem.parent = parentItem
}
}
saveContext()
}
Try this to automatically create relationships and properties.
Generating Swift models from Core Data entities
i.e.
Edit: I found the solution to generate a Swift model from Core Data entity:
On Xcode:
Editor > Create NSManagedOjbect > Click button "Next" > Click button
"Next" > Select "Swift" Langage > Click button "Create"
Here is the link on how to create a relationship from scratch. This is a very well explained and awesome tutorial. Go through this and you'll be able to understand all the things.
http://code.tutsplus.com/tutorials/core-data-from-scratch-relationships-and-more-fetching--cms-21505

Resources