Swift 4 multi dimensional array or struct? - swift

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]

Related

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

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.

Understanding LinkingObjects in Realm Xcode 12, Also when to use it

In Realm, I had problem understanding ( Im new in Realm T,T ) the implementations of LinkingObjects , let's say a Person could have more than one Dog ( List of Dog ) so I would write the code such as below:
Person : Object {
dynamic var name:String = ""
let dogs = List<Dog>()
}
Dog : Object {
dynamic var name: String = ""
let walkers = LinkingObjects<fromType: Person.self, property:"dogs">
}
lets say
Person A
dogs = [Alex,Cindy]
Person B
dogs = [Doggo,Kiba]
if I delete Alex using Realm.delete then it become Person A dogs = [Cindy]
this is quite straight forward.
but when I remove LinkingObjects from the class such as below:
Person : Object {
dynamic var name:String = ""
let dogs = List<Dog>()
}
Dog : Object {
dynamic var name: String = ""
}
the app still can run normally without any diff, I can still add Person 3 and append a list of Dog. Also when deleting ( Realm.delete ) there is no difference as when I'm adding LinkingObject to the Dog class. what's the difference here? When can I use LinkinObject? Can anyone please explain? or write some more understandable code?
Already read all the previous answer but still I need another explanation. Thank You So Much!!
You can think of LinkingObjects almost as a computed property - it automagically creates an inverse link to the parent object when the child object is added to the parent objects List.
So when a Dog is added to a person's dogs list, a person reference is added to the Dog's walkers list. Keeping in mind that it's a many to many relationship so technically if Person A adds Doggo, and Person B adds Doggo, the Doggo's inverse relationship 'walkers' will contain Person A and Person B
the app still can run normally without any diff
Which is true, it doesn't affect he operation of the app. HOWEVER the difference is that by removing the walkers LinkingObjects, there's no way to query Dogs for their Person and get Dog Results (i.e. you can't traverse the graph of the relationship back to the person)
In other words we can query Person for kinds of dog stuff
let results = realm.objects(Person.self).filter("ANY dogs.color == 'brown'")
which returns a results object contains Persons that have a brown dog. However, we don't know which dog they have is brown; could be one, could be three.
However, suppose you want to get a results object containing specific brown dogs and want the owners name for each - you can't really do that without an inverse relationship (LinkingObjects)
let dogResult = realm.object(Dog.self).filter("color == 'brown'")
for dog in dogResult {
let person = dog.walkers.first!.name //assume there is only one owner
print(person.name)
}
The dogResults will only contain brown dogs, and you know specifically which ones they are.
So that's a big difference; without LinkingObjects we rely on returning Person objects and then iterating or processing each to get to the data we want. With LinkingObjects we can specifically get the objects we want to work with.
It's super handy when you want to add an observer to results to watch for changes - say on Brown dogs - to be notified of changes.

RealmSwift - how can I query a distinct array of objects

I have the following (simplified) model
class Taxonomy: Object {
var id: ObjectId // Primary Key
var parent: Taxonomy?
var name: String
var isSelected: Bool
var children = List<Taxonomy>()
var assets = List<Asset>()
}
class Asset: Object {
var id: String // Primary Key
var name: String
let taxonomies = LinkingObjects(fromType: Taxonomy.self, property: "assets")
}
The taxonomy can be an infinite hierarchy with each item containing zero or more children. A leaf would have no children. Assets can be classified as one or more 'leaf' taxonomies.
For example Animals->Birds->"White-faced Pigeon"
There could be multiple assets, let's say images ( but could be documents, recording, etc.) that have this classification with the references stored in Taxonomy.assets array.
If the user selected 'Birds' in the hierarchy then I want to retrieve a distinct list of all the assets from the SELECTED taxonomies and all their children's assets and the children's children etc.
In general only 'leaf' taxonomy items will contain assets - but not necessarily so since some assets may for example be as yet unclassified birds and simply associated with a higher level group until further refinement is done.
For example:
let selectedTaxonomies = realm.objects(Taxonomy.self).filter("isSelected = true")
let children = selectedTaxonomies.flatMap({$0.children})
let childrensChildren = children({$0.children})
etc... (how to traverse a hierarchy of arbitrary depth)
let assets = allSelectedTaxonomies.flatMap({$0.assets}).distinct(by: "id")
Is there any way to do this in some efficient way.
EDIT:
This seems to work but not sure it is very efficient
extension Taxonomy {
var allChildren: [Taxonomy] {
return Array(children)+Array(children.flatMap({$0.allChildren}))
}
}
and then
let selectedTaxonomies = realm.objects(Taxonomy.self).filter("isSelected == true")
let allChildren = selectedTaxonomies.flatMap({$0.allChildren})
let allTaxonomies = Array(selectedTaxonomies) + Array(allChildren)
assets = assets.filter("ANY taxonomies IN %#", allTaxonomies)
Example
Here the user could select images and drag and drop them onto one or more of the classifications on the left. So the same item could appear in more than one place. For example one of the eagle images might also appear in another hierarchy called Best Of -> Birds for example. Right now I am doing a super expensive fetch of the selected taxonomy hierarchy and am trying to establish if there is a way to build a Realm query maybe using Predicates that would be faster/more efficient.

Querying in Firebase by child of child

I have a structure of objects in Firebase looking like this:
-KBP27k4iOTT2m873xSE
categories
Geography: true
Oceania: true
correctanswer: "Yaren (de facto)"
languages: "English"
question: "Nauru"
questiontype: "Text"
wronganswer1: "Majuro"
wronganswer2: "Mata-Utu"
wronganswer3: "Suva"
I'm trying to find objects by categories, so for instance I want all objects which has the category set to "Oceania".
I'm using Swift and I can't really seem to grasp the concept of how to query the data.
My query right now looks like this:
ref.queryEqualToValue("", childKey: "categories").queryOrderedByChild("Oceania")
Where ref is the reference to Firebase in that specific path.
However whatever I've tried I keep getting ALL data returned instead of the objects with category Oceania only.
My data is structured like this: baseurl/questions/
As you can see in the object example one question can have multiple categories added, so from what I've understood it's best to have a reference to the categories inside your objects.
I could change my structure to baseurl/questions/oceania/uniqueids/, but then I would get multiple entries covering the same data, but with different uniqueid, because the question would be present under both the categories oceania and geography.
By using the structure baseurl/questions/oceania/ and baseurl/questions/geography I could also just add unique ids under oceania and geography that points to a specific unique id inside baseurl/questions/uniqueids instead, but that would mean I'd have to keep track of a lot of references. Making a relations table so to speak.
I wonder if that's the way to go or? Should I restructure my data? The app isn't in production yet, so it's possible to restructure the data completely with no bigger consequences, other than I'd have to rewrite my code, that pushes data to Firebase.
Let me know, if all of this doesn't make sense and sorry for the wall of text :-)
Adding some additional code to Tim's answer for future reference.
Just use a deep query. The parent object key is not what is queried so it's 'ignored'. It doesn't matter whether it's a key generated by autoId or a dinosaur name - the query is on the child objects and the parent (key) is returned in snapshot.key.
Based on your Firebase structure, this will retrieve each child nodes where Oceania is true, one at a time:
let questionsRef = Firebase(url:"https://baseurl/questions")
questionsRef.queryOrderedByChild("categories/Oceania").queryEqualToValue(true)
.observeEventType(.ChildAdded, withBlock: { snapshot in
print(snapshot)
})
Edit: A question came up about loading all of the values at once (.value) instead of one at at time (.childAdded)
let questionsRef = Firebase(url:"https://baseurl/questions")
questionsRef.queryOrderedByChild("categories/Oceania").queryEqualToValue(true)
.observeSingleEventOfType(.Value, withBlock: { snapshot in
print(snapshot)
})
Results in (my Firebase structure is a little different but you get the idea) uid_1 did not have Oceania = true so it was omitted from the query
results.
Snap (users) {
"uid_0" = {
categories = {
Oceania = 1;
};
email = "dude#thing.com";
"first_name" = Bill;
};
"uid_2" = {
categories = {
Oceania = 1;
};
"first_name" = Peter;
};
}
I think this should work:
ref.queryOrderedByChild("categories/Oceania").queryEqualToValue(true)

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