Save array of classes into Firebase database using Swift - swift

I have an array of classes, which looks like this:
var myItems = [myClass]()
class myClass: NSObject {
var a: String?
var b: String?
var c: String?
var d: String?
}
What I want is to save the array called myItems into my database, and have every class inside of a personal section inside the database. Basically, I want every class to look like the one called "Eko" in this image:
To clarify, after "Eko" all the rest of the classes which is inside of the array myItems should be displayed. To achieve what the picture is demonstrating, I used this code:
let data = self.myItems[0]
let currU = FIRAuth.auth()?.currentUser?.uid
let userRef = self.ref.child("users").child(currU!).child(data.a!)
userRef.updateChildValues(["a": data.a!, "b": data.b!, "c": data.c!, "d": data.d!])
Obviously, this will only save the class at index 0 from the array myItems into the Firebase Database, which is displayed in the image above.
My question is thus, how do I save the entire array into the database? With my code I can only save 1 class from the array, and I would like to save all of the items into the database, so that they end up looking the same way that the one class does in the image. You could compare this to populating a tableView, where you need the "indexPath.row" to populate it with all the items instead of only one. I hope that I was clear enough!

You can't save a class into Firebase. But.. A class has a similar structure to a dictionary (properties and values, like key: value pairs etc).
Arrays in Firebase should generally be avoided - they have limited functionality and the individual elements cannot be accessed and for any changes you have to re-write the entire array.
Using a structure where the parent key names are created with childByAutoId is usually preferred.
The easiest solution is to simply add intelligence to the class so it would craft a dictionary and then save itself.
Craft a user class
UserClass
var name = String()
var food = String()
func saveToFirebase() {
let usersRef = myFirebase.child(users)
let dict = ["name": self.myName, "food", self.myFood]
let thisUserRef = usersRef.childByAutoId()
thisUserRef.setValue(dict)
}
}
and and array to store them
var usersArray = [Users]()
populate the array
var aUser = UserClass()
aUser.name = "Leroy"
aUser.food = "Pizza"
usersArray.append(aUser)
var bUser = UserClass()
bUser.name = "Billy"
bUser.food = "Tacos"
usersArray.append(bUser)
and then iterate over the array saving each user
for user in usersArray {
user.saveToFirebase()
}
this will result in
users
-Ykasokokkpoad
name: Leroy
food: Pizza
-YJlaok9sk0sd
name: Billy
food: Tacos
which is very similar to the structure you want. There are many other ways of creating this structure. For example, you could craft the entire dictionary in code and write it all out at one time.
Pardon typo's, I wrote this on the fly.

Firebase has no native support for arrays. If you store an array, it really gets stored as an "object" with integers as the key names.
// we send this
['hello', 'world']
// Firebase stores this
{0: 'hello', 1: 'world'}
Read this post for better understanding.

Related

Query MongoDB Realm array only at first index

I need to query MongoDB Realm from synced iOS and Android app. In Swift I can write something like this:
let dictionary = realm?.objects(myReamlObject.self)
let results = dictionary?.where {
$0.senses.glosses.term == "the term I want"
}
or using predicate:
let results = dictionary?.filter("ANY senses.glosses.term == %#", "the term I want")
Both work well, but I don't want to check ALL senses.glosses.term.
Every entry has (or could have) many senses and many glosses.
I would like to check term of first senses in first glosses only.
Something I would write like this:
let results = dictionary?.where {
$0.senses[0].glosses[0].term == "the term I want"
}
But it gives error:
Referencing subscript 'subscript(_:)' on 'Query' requires that
'List<myRealmObject_senses>' conform to 'RealmKeyedCollection'
Any suggestion on how to query only first index of an array in MongoDB Realm? Thank you
Let me re-state the question
How to query a Realm objects' List property - but only query on the first
element in the List.
The answer is going to depend on the amount of results being worked with.
Here's how to do it in a way that's O.K. for small datasets but NOT RECOMMENDED
Your models were not included in the question so let me use a simplified model of a PersonClass that as a List of DogClass objects
class PersonClass: Object {
#Persisted var name = ""
#Persisted var dogList = List<DogClass>()
}
class DogClass: Object {
#Persisted var name = ""
}
The idea here is to use Swift high-level functions to only test the first item in each persons doglist for a match (this can be applied to other languages as well)
//get all the people
let peopleResults = realm.objects(PersonClass.self)
//use the high-level Swift function compactMap to return all of
// the people whose first dog is named "Spot"
let persons = peopleResults.compactMap { person -> PersonClass? in
if person.dogList.first?.name == "Spot" {
return person
}
return nil
}
The downside is that this code overrides a fundamental advantage of Realm - that Realm objects are lazily loaded if Realm functions are used e.g. as soon as a high-level Swift function is used, ALL of the objects are loaded into memory, potentially overwhelming the device.
A better option is to simply add a managed property to the PersonClass that also points to the 0'th element in the list.
class PersonClass: Object {
#Persisted var name = ""
#Persisted var dogList = List<DogClass>()
#Persisted var mainDog: DogClass?
func addMainDog(withDog: DogClass) {
self.dogList.append(withDog)
self.mainDog = withDog
}
}
as you can see, there's also a function to add that first dog to the list and also populates the mainDog property which points to the same object. It's one property so the overall impact is very low but the advantages for simple queries are very high.
From there the query becomes trivial
let peopleResults = realm.objects(PersonClass.self).where { $0.mainDog.name == "Spot" }
Expanding on this, you could save the 0th element of each List object in the Parent object or even have a property in each child object that points to the first element in it's respective list.

Swift how to remove elements from an array of enums?

I have an array of enums and I'd like to remove x number of elements from it, the code below is what I'm trying to achieve, it partially works because it removes elements only from the more variable created in the switch-case but the original array doesn't change
MyArray of enums
Contacts
More
if more is present it means there are more contacts to download, when the user tap on a button it should remove ids that has been downloaded
Here is an example:
// Switch last element of my array
switch model.myArray[model.myArray.count-1] {
// If last element is More type
case var more as More:
// Download new contacts
downloadfunction()
// Remove 100 contacts that has been downloaded
let range = 0...99
more.peopleIds?.removeSubrange(range)
}
More structure
public struct More: Decodable, Identifiable {
public let id: String?
public var peopleIds: [String]?
I think the best way to check the type of the last element of the array is to cast it using an if var ...
if var more = model.myArray.last as? More {
and then change it and replace the old value in the array
if var more = myArray.last as? More, let array = more.peopleIds {
more.peopleIds = Array(array.dropFirst(100))
myArray[myArray.endIndex - 1] = more
}

Delete specific object from LinkingObjects list - Realm Swift

I am currently trying out Realm on a test project and I have been struggling with removing a specific object from a List. LensDBObject and ListDBObject. LensDBObject contains a list of lenses and ListDBObject are lists of existing lenses. A lens can be in multiple lists and I'd like to remove a specific lens from a specific list but not remove if from the other lists.
Below are my two classes:
#objcMembers class LensDBObject: Object {
dynamic var id = UUID().uuidString
dynamic var manufacturer = ""
dynamic var series = ""
dynamic var serial = ""
dynamic var isSelected = false
override static func primaryKey() -> String? {
return "id"
}
let objects = LinkingObjects(fromType: ListDBObject.self, property: "lensList")
}
#objcMembers class ListDBObject: Object {
dynamic var listName = ""
let lensList = List<LensDBObject>()
}
Below is my code to find a specific lens in the list I want. The values returned are what I expect.
let listToSearch = realm.objects(ListDBObject.self).filter("listName == %#", "List 542")
print(listToSearch)
let filteredResults = listToSearch[0].lensList.filter("manufacturer == %# AND series == %# AND serial == %#", "Panavision" , "Primo Prime", "407")
print(filteredResults)
However, when I try to delete filteredResults, it deletes it from the lensDBOject altogether. I just want to be able to delete this specific lens from this specific list.
try! realm.write {
realm.delete(filteredResults)
}
I tried using for loops to get the index of the lens in the list and then delete it directly from that. But it still deletes the lens everywhere.
Am I missing something? Should I be using a one-to-many relationship as opposed to a LinkingObject?
Thanks for you help!
Try something like this. You only want to remove the lens from the list, not delete it from the Realm.
try! realm.write {
filteredResults.forEach { lens in
if let index = listToSearch[0].lensList.index(of: lens) {
listToSearch[0].lensList.remove(at: index)
}
}
}
Note that this will remove from that one specific list all lenses that match your filter.
Edit: Updated to reflect Realm's custom List class.
The if let is required, because index(of:) could potentially return nil if the object is not found in the list. Additionally, we must do it one item at a time rather than getting all the indexes first, since removing an item would cause the index array to be wrong.

Storing and accessing object array in dictionary - Swift 3 - macOS

So I'm not 100% sure if this is even the right way about this but I have a class that stores a few variables, I then have an array that stores theses objects.
I've been trying to put these objects into a dictionary for easy understanding so that I don't have to have several arrays, the idea being I could use the dictionary key to identify which set of arrays I want.
For example I have
class Player {
init(FirstName: String, LastName:String, Team: String, Pos: String, Line: Int, GaP: Int, Sh: Int, ShotPerc: Float, TOIPerG: Float, ShiPerG: Float, TOIPerShi: Float, FaceOffPer: Float, HitA: Int, HitF: Int, SavePerc: Float){
lastName = LastName
firstName = FirstName
team = Team
pos = Pos
line = Line
GP = GaP
sh = Sh
shotPerc = ShotPerc
TOIPerGame = TOIPerG
shiftsPerGame = ShiPerG
TOIPerShift = TOIPerShi
faceOffPerc = FaceOffPer
hitA = HitA
hitF = HitF
savePerc = SavePerc
}
I then store these in separate arrays filtered by the team (only showing relevant code but I parse a CSV file into array of Player and then use filter to put them into appropriate array)
players.append(Player(FirstName: record["First Name"]!, LastName: record["Last Name"]!, Team: record["Team"]!, Pos: record["Pos"]!, Line: Int(record["Line"]!)!, GaP: Int(record["GP"]!)!, Sh: Int(record["Sh"]!)!, ShotPerc: Float(record["ShotPerc"]!)!, TOIPerG: Float(record["TOIPerGame"]!)!, ShiPerG: Float(record["ShiftsPerG"]!)!, TOIPerShi: Float(record["TOIPerShift"]!)!, FaceOffPer: Float(record["FOffPerc"]!)!, HitA: Int(record["HitA"]!)!, HitF: Int(record["HitF"]!)!, SavePerc: Float(record["SavePerc"]!)!))
var FLA = [Player]()
var TBL = [Player]()
var DET = [Player]()
var WSH = [Player]()
var PIT = [Player]()
var NYR = [Player]()
var NYI = [Player]()
This seem messy ergo wanting to put them all into a dictionary but I can only get one object into it, presumably because it's wiping the key. My question is how do I get it to store all of the players under the same key?
var dict: [String: Array<Player>] = [:]
for (player, _) in LAK.enumerated(){
dict = ["LAK":[LAK[player]]]
}
print(dict["LAK"]?[0].fullName())
I'm presuming it's more along the lines of
dict["LAK"] =
but I feel like I'm going in circles!
Thanks!
You cannot store multiple values under the same key. That defeats the whole purpose of Key-Value storage. One key should map to one value.
You can store the array of your players instead, but that kind of defeats the purpose of using a dictionary.
Dictionaries should be used when you have unique keys (identifiers) for objects that you want them accessible under. In your case, you could use the player names for that, provided that they are all unique.
If you want to store to store all players under same key then first collect all players and then add it to the corresponding key.
let suppose you read player from csv. I am writing pseudo code.
// For playerData in readPlayerFormCSV()
// Now you will create a Player Object using data
// player = Player(playerData)
// One you have the player object
// create the dictionary where you want to store them
var dict: [String: Array<Player>] = [:]
// Check which team the player belongs
// if player belongs to LAK then add it player array with team key.
dict["LAK"].append(player)
My question is how do I get it to store all of the players under the same key?
If you just want players under a single key, Just add it to the corresponding array. I would not recommend this because the purpose of Dictionary is to store "Key-Value" pairs and adding just a single key doesn't make sense. It better to use an array.
dict["Your Key"].append(player)

How to save in NSUserDefaults a array of object?

I need save in NSUserDefaults an array or other structure of data to save objects with this format:
name = "myName", lastName = "myLastName"
I was trying to do this with an array of arrays but this can't save in NSUserDefaults.
Now I am trying with a custom class with structure to create objects and append to the array, but also get an error when trying to save this to NSUserDefaults. I need save with this structure, but I don't know what is the correct form. How would I do this?
var taskMgr: TaskManager = TaskManager()
struct task {
var name = "Un-Named"
var lastName = "Un-lastNamed"
}
class TaskManager: NSObject {
var tasks : [task] = []
func addTask(name : String, lastName: String){
tasks.append(task(name: name, lastName: desc))
}
}
NSUserDefaults expects the stored objects to all be property list objects, i.e. instances of NSArray, NSDictionary, NSString, NSNumber, NSData, or NSDate. Your struct isn't one of those. You'll need to convert your objects to property list types before you store them. One way to do that is to use NSKeyedArchiver and the NSCoding protocol to write your structs into an instance of NSData.