Filter BehaviorRelay array rxswift - ios

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")}

Related

What would be a good way to partition instances by a particular matching property, such that each partition is a collection view section?

Say that in this example here, the struct
struct Reminder: Identifiable {
var id: String = UUID().uuidString
var title: String
var dueDate: Date
var notes: String? = nil
var isComplete: Bool = false
var city: String
}
is modified slightly to include a city string. In the collection view that displays the reminders, I'd like each section to be each unique city, so if two reminder cells have the same city string then they would be in the same section of the collection view.
The progress I've made to this end is in sorting the reminders array so that reminders cells are grouped together by city
func updateSnapshot(reloading ids: [Reminder.ID] = []) {
var snapshot = Snapshot()
snapshot.appendSections([0])
let reminders = reminders.sorted { $0.city }
snapshot.appendItems(reminders.map { $0.id })
if !ids.isEmpty {
snapshot.reloadItems(ids)
}
dataSource.apply(snapshot)
}
Where I'm stuck is in coming up with a way to make the snapshot represent sections by unique cities, and not just one flat section of all reminders.

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

SwiftUI How to get changes of computed Property of NSManagedObject subclass

I am using a List to display a CoreData one-to-many relationship models. The list displayed the computed property. When the NSManagedObject was changed, the computed property doesn't know about it.
Let's say People has many Books. Like the code below:
People+CoreDataProperties.swift
extension People {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Contract> {
return NSFetchRequest<People>(entityName: "People")
}
#NSManaged public var peopleID: String?
#NSManaged public var name: String?
#NSManaged public var books: Set<Book>?
#NSManaged public var bookOrders: [String]
}
The bookOrders is the ordered id of the books.
BookListView.swift
struct BookListView: View {
#state var people: People //this is from #FetchRequest
List {
ForEach(people.sortedBooks.indices, id:\.self) { index in
BookRowView(bookListItem: viewModel.books[index])
}
}
}
Here is the extension of People:
extension People {
var sortedBooks: [Book] {
let contractMapping = books.reduce(into: [String: Book]()) { (result, book) in
result[book.bookID] = book
}
return bookOrders.map {
contractMapping[$0]
}.compactMap { $0 }
}
So when I reorder the books, which say update the bookOrders or there's a background request update of adding the new books then how can I notify BookListView update the view?
Inside the BookListView declare your People as ObservedObject
struct BookListView: View {
#ObservedObject var people: People // << now View updates for any changes here

Filter Array by "isComplete"

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

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

Resources