Fill array with objects - swift

I have created a cities class, which consists of several City objects, each with specific data. Since the data is fixed, I want to assign it in the init() function of the Cities object.
// CASE 1 - ERROR
class Cities : NSObject {
var cityList:[City]
override init() {
cityList = []
let city = City()
city.fill("LA" , "USA")
self.cityList.append(city)
city.fill("Amsterdam" ,"Netherlands")
self.cityList.append(city)
city.fill("Beijing" , "China")
self.cityList.append(city)
Result: Beijing Beijing Beijing
// CASE 2 - CORRECT
class Cities : NSObject {
var cityList:[City]
override init() {
cityList = []
var city:City
city = City(name: "LA" ,country: "USA")
self.cityList.append(city)
city = City(name: "Amsterdam", country: "Netherlands")
self.cityList.append(city)
city = City(name: "Beijing" , country: "China")
self.cityList.append(city)
Result: LA, Amsterdam, Beijing
When I run this first script, I get a nice Cities object and the array has 3 cities in it, all Beijing.
In the Correct case, the data is assigned by the init function of object City. Now everything works as expected.
In both cases I have created only one City object: city. Why is this difference in Swift behaviour?
Using Apple Swift version 2.1

In Case 1 you have only created one City object, this is the line which create the object:
let city = City()
City is a class, which is a reference type. It means it points you to the same place in memory. Your first call to:
city.fill("LA" , "USA")
set the properties to 'LA' and 'USA', the second call
city.fill("Amsterdam" ,"Netherlands")
go to the same place in memory and override the properties with the new values and the same happens with the 3rd call which is the last call so you end up with the values.
Your array cityList has three items which points to the same place in memory this is why you see the same object:
cityList[0] === cityList[1] === cityList[2]
In Case 2 every time you create new object you call the constructor, for example:
City(name: "LA" ,country: "USA")
Constructor allocate new memory address which ends up with completely new object, so every time you call:
City(name:...)
your city object points to different place in memory.

In first case you create single City object and then fill it with different data, but it is one object that you created it once and simply placed the three pointer to it in the array, when you call fill you change the data inside this single object and in the end of your init you have array with 3 pointers to one object that contains last data
In second case you create 3 different objects and add it to array

Related

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.

Firebase BarcodeScanner get parent/other nodes of a specific child

In my app I have certain items in my firebase database like this:
Categories
Category1
Item1
id: item1
name: Item 1 name
barcode: 473088034839
item2
id: item2
name: Item 2 name
barcode: 564084724885
These items are in a collectionview. I have another view where I'm using a barcodeScanner to scan the barcode of the products in my database. The barcode scanner works great and I'm able to print the barcode of scanned products to the console, and it matches the codes in "barcode" in my database.
The problem is I'm trying to get the name if the item I'm scanning. With the following code I'm able to find/match the barcode I'm scanning with the code in my database:
let someBarcode = Database.database().reference().child("Categories").queryOrdered(byChild: "barcode").queryEqual(toValue: code)
print(someBarcode)
code is the string of numbers I get from scanning a barcode. After finding the correct barcode in my database, how can I then retrieve the current Items id and/or name?
Firebase Realtime Database queries work on a flat list. They can order/filter on a property at a fixed path under each direct child under the location you query on.
So in your scenario you could query across all categories on a fixed property of each category, or you can query a specific category for items with a specific property value. But you can't search across all categories for the ones that contain an item with a certain property value.
The solution is to create an data structure that allows the specific query, typically either by flattening the list you already have, or by adding an additional structure to allow the lookup.
For more on this, see:
Firebase Query Double Nested
Firebase query if child of child contains a value
With Franks help I managed to create a new node in my firebase DB with the barcode codes:
Barcodes
item1 : code1
item2 : code2
item3 : code3
Then I used the following function to
func scanner(_ controller: BarcodeScannerViewController, didCaptureCode code: String, type: String) {
// get product id from Barcodes
Database.database().reference().child("barcodes")
.observeSingleEvent(of: .value) { (snapshot) in
guard let dict = snapshot.value as? [String: Any] else {
self.showNotFoundMessage()
return
}
for (key, value) in dict {
let valueString = "\(value)"
if valueString == code {
self.getProduct(id: key)
return
}
}
self.showNotFoundMessage()
}
}

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]

Understanding equality in RealmSwift

I have some items which I fetch from Realm:
let items = realm.objects(ShoppingListItem.self)
print("\(items.count) items") // 10 items
Each item has a subdepartment, and each subdepartment has a department:
let departments = items.flatMap({ $0.product?.subdepartment?.department })
print("\(departments.count) departments") // 10 departments
My goal is to find the unique Department objects from this list. The expected result is 4. My standard approach here is to use Set:
let uniqueDepartments1 = Set(departments)
print("\(uniqueDepartments1.count) unique departments via Set") // 9 unique departments via Set - but changes every time!?
I figure there must be something I'm missing related to the way Realm treats equality. But to check, I also attempt to get the unique departments via enumeration:
var uniqueDepartments2 = [Department]()
for department in departments {
if uniqueDepartments2.contains(department) {
continue
}
uniqueDepartments2.append(department)
}
print("\(uniqueDepartments2.count) unique departments via enumeration") // 4 unique departments via enumeration
This is indeed the expected result.
Why doesn't Set work here as I expected it to? And why is the count changing each time I run it?
Edit 2/27
Here are the models in play:
class ShoppingListItem: Object {
dynamic var product: Product?
convenience init(ingredient: Ingredient) {
self.init()
self.product = ingredient.product
}
}
class Product: Object {
dynamic var productID, subdepartmentID, title: String?
dynamic var subdepartment: Subdepartment?
}
class Department: Object {
dynamic var departmentID, title: String?
}
class Subdepartment: Object {
dynamic var subdepartmentID, departmentID, title: String?
dynamic var department: Department?
}
In short: in order for an Object subclass to be correctly hashable you must currently declare a property as the primary key.
Set is built on top of a hash table. This means it checks for the existence of a specific object by first computing the hash value of the object, mapping that hash value to a bucket within the hash table, and then checking each entry in that bucket for equality with the specified object.
The nature of this implementation means that for Set to work correctly on objects of a given type, both the hashValue property and == operator must obey specific rules. In particular, any two objects for which == returns true must return the same value from their hashValue property (the reverse is not required; it's completely valid for two unequal objects to have the same hashValue). Realm's implementation's of hashValue and == don't currently meet this criteria unless your class declares a property as the primary key. When no primary key is declared, Object's default hashValue calculation defers to -[NSObject hash], which simply returns the object's address in memory. Since Object.== allows two objects with different addresses to compare equal, this violates the relationship between hashValue and == I outlined above, leading to incorrect behavior when used with Set or as the key of a Dictionary. I've filed a bug against Realm requesting that the behavior of == be fixed to be compatible with the value returned by the hashValue property.