Core Data: How can one get updates to entities of a relationship? - swift

I know that you can observe changes to entities you've fetched with #ObservedObject.
The problem is best illustrated with an example: If I had an entity school with a one to many relationship to students and each student has a property numberOfPencils then I want to recalculate the sum of all pencils within a school whenever a student in the school changes his numberOfPencils property.
If I access all the students through the schools entity (which is an #ObservedObject) then I only get updates to the sum if a new student is added but not when an existing student changes his numberOfPencils because student objects can only be observed one by one as far as I know.
The only thing I could come up with is setting up a NSFetchedResultsControllerDelegate with a fetchRequestPredicate that gets all the students associated with a school through some keyPath from student to school which uniquely identifies the school. But that seems like a hacky way to do it and there surely must be a better solution that is eluding me.
Basically I want to get notified of changes to any of the students associated with a school.

Derived properties is definely a good way to go.
But to clarify: #ObservedObject can also get updated on change of the item. You just have to manually trigger the update with objectWillChange.send()
Say your School cell looks like this:
struct SchoolCell: View {
#ObservedObject var school: School
var body: some View {
HStack {
let pencils = school.pupilsArray.reduce(0, {$0 + $1.numberOfPencils})
Text(school.name)
Spacer()
Text(verbatim: "\(pencils) pencils")
.foregroundColor(.secondary)
}
}
}
then the Pupil modify would work like this:
// ...
#ObservedObject var school: School
// ...
Button("+10 Pencils") {
school.objectWillChange.send()
pupil.numberOfPencils += 10
try? viewContext.save()
}
It is important though that SchoolCell IS an own struct, so it gets refreshed when its #ObservedObject school changes.

Related

How to filter with NSPredicate by an attribute of an Element I pass from the parent view?

Please help me with the following question - how do I filter a fetchRequest by an attribute of an Element I pass from the parent view?
import SwiftUI
struct aListDetailView: View {
#State var aParameter: String
#FetchRequest(sortDescriptors: [], predicate: NSPredicate(format: "elementIdentificationAttribute == %#", aParameter)) var contracts: FetchedResults<Contract>
var body: some View {
List {...}
...}
I get "Cannot use instance member 'aContract' within property initializer; property initializers run before 'self' is available".
I understand why, but how do I bypass this?
My example is a simplified version.
In truth, my query is this: I have a list of Contract elements, and each contract has a list of invoices issued in regards to that specific contract. Each contract has a unique serial code I give it, as an attribute.
My #FetchRequest for a list of all Contract elements is in Content View. I loop through them to create a list. Each row of the list is a NavigationLink to a new view, and I pass the specific contract element to the new view.
In the new view, I need to fetch a list of all the invoices specific to that contract.
However, when I add a new invoice to create a new row in the invoice sub-view list, core data does not show the new row, for a new invoice. The new row appears only when I return to the Content View and back to the invoice view, because my #FetchRequest for Contracts is in Content View.
I thought I could bypass this issue through a #FetchRequest for a list of contracts that have an attribute of my choosing (say, an attribute contractSerialNumber) equal to the attribute instance of the contract I pass to this view.
Something like:
#FetchRequest that filters all contracts in storage by the specific, unique attribute of my choosing of the contract I passed from the Parent View. Places them in a list.
ForEach contract that has that particular attribute (which will be a list of one contract) {
ForEach contract.invoiceArray { invoice in
Text(contract.invoice.invoiceIdentificationAttribute
} }
Thank you!

Swift 4 multi dimensional array or struct?

I'm creating a table viewcell. this viewcell will show hotel and relative rooms .
to do that I created 2 classes to define hotel and rooms. each of 2 classes represent the relative object. example:
hotel: services, position, lat, lang , description
room: bed_type, people, Availability
I know need now to create a structure or array or whatever that act like a three so basically so basically , for each hotel I can have more than one available room.
I was looking using an 2d array of any object as first solution but I don't know If this is best way to solve the problem.
ideally best would be to later can access object in an easy way...
my first second idea is to use a struct like this
struct SearchResults{
var Hotel: HotelModel
var Rooms: [RoomModel]
}
any suggestion is welcome
Here is how you can model your data.
Just create a Hotel model that contains an array of Room as its property.
struct Hotel {
var rooms: [Room]
}
struct Room {
//your properties...
}
Now, instead of using an extra SearchResults model, you can simply use a array of Hotel, i.e.
var results: [Hotel]

Should I create a separate Realm object for custom grouping?

I have been using Realm in an app and love it. Thank you! I have a question I would like to run by you folks and get some advice.
Lets say you have a realm object that contains a date field (simplified example):
class Appointment: Object {
dynamic var type = ""
dynamic var date = Date()
}
Now, suppose you have saved thousands of Appointments, and you are going to display these on a tableview or collectionview, grouped by week for example. So the datasource for your view would be something like this.
struct AppointmentsInWeek {
var startDate: Date?
var endDate: Date?
var appointments: [Appointment]
}
So, I have two options in mind I am thinking through with various pros and cons:

A) Make AppointmentsInWeek a subclass of Object, save it in the Realm, and use that as the datasource.
PROS:
Data for table will be lazy loaded from Realm
Simple to use at the moment it is needed.
CONS:
Keeping this up to date seems like a challenge. I would probably have some kind of observable looking at the Appointment in Realm and as any are added put them in the appropriate AppointmentWeek
B) Upon loading the screen with the tableview, fetch all appointments, or a subset of them, group them by their appropriate start and end date, and create an AppointmentsInWeek struct to use as the datasource.
PROS:
AppointmentsInWeek will always be up to date because it is created on the fly as needed
CONS:
We would have to keep all of this in memory, limiting the amount of appointments we could realistically display at once.
I started with option B but I am thinking now it might be better to go with option A. If I do, the biggest issue is making sure the Realm is always up to date when new appointments are added.
Questions
Are there other options I did not consider?
Which sounds like a better option?
Assuming I go with option A, would it make sense to have a class, that lives throughout the life of the app, in charge of observing the Appointments in Realm and when one is added (or changed), add it also to the appropriate AppointmentWeek?
Both options are fine, but I suggest option A. There are a few ways to approach option A.
First of all, you don't need to keep Appointment and AppointmentsInWeek in sync manually. You can use some combination of object properties, list properties, and linking objects properties to model connections between Appointments and AppointmentsInWeeks. Exactly how you might implement this depends on your app's specific needs, but here's one possibility:
class Appointment : Object {
dynamic var type = ""
dynamic var date = NSDate()
// Link to the week this appointment lives in, if desired
var week: AppointmentsInWeek? = nil
}
class AppointmentsInWeek : Object {
dynamic var startDate = NSDate()
dynamic var endDate = NSDate()
// Add appointment to List whenever it's created
let appointments = List<Appointment>()
}
A second possibility is to not use relationships at all, but to use queries instead. Realm supports queries through the Results class. You could add an ignored property on your AppointmentsInWeek class that queries the Realm for all appointments that fall within its date range:
class Appointment : Object {
dynamic var type = ""
dynamic var date = NSDate()
}
class AppointmentsInWeek : Object {
dynamic var startDate = NSDate()
dynamic var endDate = NSDate()
lazy var appointments: Results<Appointment> = {
// This implementation assumes you can get a reference
// to the Realm storing your Appointments somehow
return appointmentsRealm.objects(Appointments.self).filter(NSPredicate(format: "(date >= %#) AND (date < %#)", self.startDate, self.endDate))
}()
override static func ignoredProperties() -> [String] {
return ["appointments"]
}
}
In either case Realms, lists, and results all update automatically whenever the runloop of the thread they are on (usually the main thread) runs. Realm also supports a variety of notifications in case you need to react to changes manually.

Best Practice for saving many to one in order using NSSet?

I am new to Swift and was wondering if anyone can give me some tips on how to store data in a Core Data NSSet sorted for an item that has the relationship one to many in order. I have a book class (the many) and I have a shelf class (the one) that has an NSSet of books in it.
class Shelf: NSManagedObject {
#NSManaged var books: NSSet
}
class Book: NSManagedObject {
#NSManaged var title: String
}
I can add books to the shelf and that works, but the order seems to be random every time I try to access them instead of the order I added them. I want the result in the order I added it to be retained. Is there a way to do this without adding another class like bookOnShelf to store the position?
var booksInOrder = myShelf.books.AsAnyObject as [Book]
for(var i = 0; i < mySlelf.books.count; i++)
{
output(booksInOrder[i])
}
Update your CoreData model to enable the "Ordered" attribute of the relationship.
You'll also want to change the property declaration in your code to:
#NSManaged var books: NSOrderedSet

ASP.NET MVC 3: ViewModel that deals with a list of lists

I'm trying to put together a ViewModel that will have a list of users and each user will have a list of locations.
The User table and Location table are joined together through another table that holds each respective ID and some other information. This table is essentially a many to many join table.
I've tried a few different viewModel approaches and they we're severely lacking... What would be the best approach for displaying this type of information?
I assume that the issue is that you want to be able to access the collection by either User or Location. One approach could be to use ILookup<> classes. You'd start with the many-to-many collection and produce the lookups like this:
var lookupByUser = userLocations.ToLookup(ul => ul.User);
var lookupByLocation = userLocations.ToLookup(ul => ul.Location);
Update:
Per your description, it seems like you don't really need to have a full many-to-many relationship in your ViewModel. Rather, your ViewModel could have a structure like this:
public class YourViewModel
{
public IEnumerable<UserViewModel> Users { get; set; }
}
public class UserViewModel
{
// User-related stuff
public IEnumerable<LocationViewModel> Locations { get; set; }
}
If you wanted to avoid redundant LocationViewModel objects, you could pre-build a mapping between your Model and ViewModel objects:
var locationViewModels = myLocations.ToDictionary(
loc => loc, loc => CreateLocationViewModel(loc));
And then reuse these objects when populating your page's ViewModel.