3

How would I achieve this setup in Firebase Realtime Database with Swift:

Database hierarchy

enter image description here

Currently, I am doing this by storing the larger element (with properties familyKey, geofences, and phoneNumbers) as a custom object. Also, the geofences property itself is an array of custom objects. I get an NSException doing this in the described fashion. How else would I go about doing this?

    var tempGeofences = [GeofenceData]()
    tempGeofences.append(GeofenceData(name: "Hello WOrld", latitude: 0, longitude: 0, radius: 1000))

    let familyKey:String = String(Int.random(in: 1000...99999))
    let uid:String = Auth.auth().currentUser!.uid
    let phoneNumber = "1111111111"
    let parent = Parent(phoneNumber: phoneNumber, familyKey: familyKey, geofences: tempGeofences)

    databaseRef.child(uid).setValue(parent)

The NSException is thrown on this line:

databaseRef.child(uid).setValue(parent)

Parent class:

import Foundation

public class Parent {
var phoneNumber: String?
var familyKey: String?
var geofences: [GeofenceData]?

init() {
    self.phoneNumber = ""
    self.familyKey = ""
    self.geofences = nil
}

init(phoneNumber: String?, familyKey: String?, geofences:[GeofenceData]) {
    self.phoneNumber = phoneNumber
    self.familyKey = familyKey
    self.geofences = geofences
}

public func getPhoneNumber() -> String {
    return phoneNumber!
}

public func getFamilyKey() -> String {
    return familyKey!
}

public func getGeofences() -> [GeofenceData] {
    return geofences!
}

// left off here, trying to send geofence object to firebase
public func toDictionary() -> Any {
    return ["familyKey": familyKey, "geofences": geofences, "phoneNumber": phoneNumber]
}

}

And the GeofenceData class:

import Foundation
import Firebase

public class GeofenceData {
var name: String?
var latitude: Double?
var longitude: Double?
var radius: Float?

init() {

}

init(name: String?, latitude: Double, longitude: Double, radius: Float) {
    self.name = name
    self.latitude = latitude
    self.longitude = longitude
    self.radius = radius
}

// left off here, trying to send geofence object to firebase
public func toDictionary() -> Any {
    return ["name": name, "latitude": latitude, "longitude": longitude, "radius": radius]
}

public func getName() -> String {
    return name!
}

public func getLatitude() -> Double {
    return latitude!
}

public func getLongitude() -> Double {
    return longitude!
}

public func getRadius() -> Float {
    return radius!
}

public func setName(name: String?) {
    self.name = name
}

public func saveToFirebase(reference: DatabaseReference) {
    let dict = ["name": name, "latitude": latitude, "longitude": longitude, "radius": radius] as Any
    reference.child("geofences").child("0").setValue(dict)
}

}

6
  • Please add your code in question and mention where you are getting NSException so that we can help you. Commented Mar 7, 2019 at 3:23
  • Hi Bhaumik, I am out of town at the moment, but I can give you as much detail as possible. Basically, I have a class that has properties of familyKey, geofences, and phoneNumber, where geofences is an array of another custom object, which has properties longitude, latitude, name, and radius. The NSException is thrown on the line where I am am saving my data to firebase via setValue(object) Commented Mar 8, 2019 at 12:38
  • The error is because you cannot store user defined objects in Firebase, OR object is nil. However, without seeing your code we're just guessing. Please take a moment and review How do I ask a good question? and How to create a Minimal, Complete, and Verifiable example. Update your question with your code and we'll take a look! Commented Mar 8, 2019 at 16:11
  • Hi, I just updated my post! Commented Mar 8, 2019 at 22:24
  • As you can see, in the saveToFirebase() function for a GeofenceData object, I had to hardcode the index 0 to create the array of GeofenceData objects in Firebase. In Android, I could just do "setValue(parent)" and it would work perfectly fine. Commented Mar 8, 2019 at 22:30

1 Answer 1

1

Parent is not an object that Firebase recognizes so it throws an error.

The Firebase guide Reading & Writing Data shows the four types of objects that can be written; String, Number, Dictionary, Array.

One solution is to build a function into the class that returns the data you want to write.

public class Parent {
    var phoneNumber: String?
    var familyKey: String?
    var geofences: [GeofenceData]?

    init() {
        self.phoneNumber = ""
        self.familyKey = ""
        self.geofences = nil
    }

    //other init functions

    func getParentDict() -> [String: Any] {

        let geoDict = ["name": name,
                       "latitude": latitude,
                       "longitude": longitude,
                       "radius": radius
        ]

        let zeroNode = ["0": geoDict]

        let dictForFirebase: [String: Any] = [
            "phoneNumber": phoneNumber,
            "familyKey": familyKey,
            "geofences": zeroNode
        ]

        return dictForFirebase
    }
}

and in practice

var tempGeofences = [GeofenceData]()
tempGeofences.append(GeofenceData(name: "Hello WOrld", latitude: 0, longitude: 0, radius: 1000))

let familyKey:String = String(Int.random(in: 1000...99999))
let uid:String = Auth.auth().currentUser!.uid
let phoneNumber = "1111111111"
let parent = Parent(phoneNumber: phoneNumber, familyKey: familyKey, geofences: tempGeofences)
let parentDict = parent.getParentDict
databaseRef.child(uid).setValue(parentDict)

However, one concern is the child node with "0" as the key. That looks like you may be using an array. If there's a good reason that's fine but there are usually much better alternatives to using array's in NoSQL databases. See the legacy but still accurate Firebase post called Arrays Are Evil

EDIT:

Per a comment/question 'how to add another child node following the "0" node"

Assume we know the parent node, qSaEE..., lets add a "1" node

let parentNode = "qSaEE..."
let geofenceRef = firebaseRef.child(parentNode).child("geofences")

let geoDict = ["name": name,
               "latitude": latitude,
               "longitude": longitude,
               "radius": radius
               ]

let oneNode = ["1": geoDict]

geofenceNode.setValue(oneNode)
Sign up to request clarification or add additional context in comments.

7 Comments

Hi Jay, when I upload the Parent object from Android, it automatically adds the "0" node seeing that "geofences" is an Array of GeofenceData objects. To parallel android, I would need to replicate this database structure for iOS. How would I do that?
Also, in my bigger node, my three properties are familyKey, geofences, and phoneNumber. Wouldn't the setup you provided give me three properties of familyKey, 0, and phoneNumber?
One last thing, I want a Parent object to be able to hold multiple GeofenceData objects, hence why I am using an array of GeofenceData objects as a property of a Parent. In the example above, we are only creating a dictionary for one Geofence data and storing that under the node "0". How would I make it flexible enough, so if I wanted to add to that array at a later point, I could add another GeofenceData object under the node "0"? Sorry for the hassle.
@pavpanda I missed that 0 node so I updated the code to provide the exact structure.
@pavpanda and I updated it to answer your second question about adding another node following the "0" node
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.