Initialize an object to take in optional values on Firebase with Swift - ios

I'm comfortable with initializing objects in general but my method only seems to work when all parameters must exist on the database. I have looked at this which works. But I now don't know how to initialize it with a nest array.
This is how I would normally initialise an object:
class User {
var firstName: String
var lastName: String
var summary: String?
var groups = [Group]()
var ref: DatabaseReference?
var key: String
init?(from snapshot: DataSnapshot) {
let snapshotValue = snapshot.value as? [String: Any]
guard let firstName = snapshotValue?["firstName"] as? String, let lastName = snapshotValue?["lastName"] as? String, let summary = snapshotValue?["summary"] as? String else { return nil }
self.firstName = firstName
self.lastName = lastName
self.summary = summary
self.key = snapshot.key
self.groups(snapshot: snapshot.childSnapshot(forPath: "groups"))
}
func groups(snapshot: DataSnapshot) {
for child in snapshot.children {
guard let group = child as? DataSnapshot else { continue }
}
}

Just don't make the optional properties a part of that guard (that's what prevents the execution to finish it:
init?(from snapshot: DataSnapshot) {
let snapshotValue = snapshot.value as? [String: Any]
guard let firstName = snapshotValue?["firstName"] as? String,
let lastName = snapshotValue?["lastName"] as? String else { return nil }
self.firstName = firstName
self.summary = snapshotValue?["summary"] as? String
self.key = snapshot.key
self.groups(snapshot: snapshot.childSnapshot(forPath: "groups"))
}
Optionally, you can use if let syntax:
if let summary = snapshotValue?["summary"] as? String {
self.summary = summary
}
But in this case this is simpler:
self.summary = snapshotValue?["summary"] as? String

Related

manually entered data in firebase issue

I am manually entering in data into my database and the only variable not getting passed from my database is the author and I do not know where I am going wrong.
func getAllArticles(handler: #escaping (_ articles: [Article])-> ()){
var articleArray = [Article]()
REF_ARTICLES.observeSingleEvent(of: .value) { (articleMessageSnapshot) in
guard let articleMessageSnapshot = articleMessageSnapshot.children.allObjects as? [DataSnapshot] else {return}
for article in articleMessageSnapshot {
let content = article.childSnapshot(forPath: "content").value as? String ?? "no content"
let author = article.childSnapshot(forPath: "author").value as? String ?? "no author"
let twitterHandle = article.childSnapshot(forPath: "twitterHandle").value as? String ?? "none"
let articleTitle = article.childSnapshot(forPath: "articleTitle").value as? String ?? "no title"
let date = article.childSnapshot(forPath: "date").value as? String ?? "no date"
let article = Article(content: content, author: author, twitterHandle: twitterHandle, ArticleTitle: articleTitle, date: date)
articleArray.append(article)
}
handler(articleArray)
}
}
Please check out below code
var articleArray = [Article]()
//REF_ARTICLES
let ref = Database.database().reference().child(“articles”)
ref.observe(.childAdded, with: { (snapshot) in
print(snapshot)
guard let dictionary = snapshot.value as? [String : AnyObject] else {
return
}
let articleObj = Article()
articleObj.Content = dictionary["content"] as? String
articleObj.Author = dictionary["author"] as? String
articleObj.Twitterhandle = dictionary["twitterHandle"] as? String
articleObj.Title = dictionary["articleTitle"] as? String
articleObj.Date = dictionary["date"] as? String
self. articleArray.append(articleObj)
}, withCancel: nil)
}
I am also working on similar app where i am storing data to firebase and retrieving. Below approach i used to fetch the data from firebase database. Please try once.
func getAllArticles(handler: #escaping (_ articles: [Article])-> ()) {
Database.database().reference().child("Articles").observe(.childAdded, with: { (snapshot) in
print("articles = \(snapshot)")
if let dict = snapshot.value as? [String: Any] {
let article = Article()
article.articleTitle = dict["articleTitle"] as? String
article.author = dict["author"] as? String
article.twitterHandle = dict["twitterHandle"] as? String
article.date = dict["date"] as? String
article.content = dict["content"] as? String
self.articleArray.append(article)
}
handler(articleArray)
}, withCancel: nil)
}
im not sure what the underlying issue was, but i fixed it by deleting "author" from the firebase tree and then adding it back

Let constant issue

I have this function that is grabbing events from firebase that a specific user is attending. Everything seems to be working up until the point where I try to alter an element relative to the event snapshot.
I get the error
Cannot assign to property: 'event' is a 'let' constant
//will show the vents that a user is attending
static func Events(for user: User, completion: #escaping ([Event]) -> Void)
{
var currentEvents = [Event]()
//Getting firebase root directory
let ref = Database.database().reference().child("users").child(user.uid).child("Attending")
ref.observe(.value, with: { (snapshot) in
// print(snapshot)
// guard snapshot.children.allObjects is [DataSnapshot] else {
// return completion([])
// }
guard let eventDictionary = snapshot.value as? [String: Any] else {
return completion([])
}
// print(snapshot)
let dispatchGroup = DispatchGroup()
eventDictionary.forEach({ (key,value) in
// print(key)
// print(value)
EventService.show(forEventKey: key , completion: { (event) in
dispatchGroup.enter()
AttendService.isEventAttended(event, byCurrentUserWithCompletion: { (isAttended) in
//error happens here
event?.isAttending = isAttended
dispatchGroup.leave()
})
currentEvents.append(.init(currentEventKey: key , dictionary: (event?.eventDictionary)!))
completion(currentEvents)
})
})
})
}
It is really confusing to me because I can't see where I declared it as a let constant.
This is the code for the method I am using to gather event info
static func show(forEventKey eventKey: String, completion: #escaping (Event?) -> Void) {
// print(eventKey)
let ref = Database.database().reference().child("events").child(eventKey)
// print(eventKey)
//pull everything
ref.observeSingleEvent(of: .value, andPreviousSiblingKeyWith: { (snapshot,eventKey) in
// print(snapshot.value ?? "")
guard let event = Event(snapshot: snapshot) else {
return completion(nil)
}
completion(event)
})
}
Below is my model for an event object
import Foundation
import FirebaseDatabase.FIRDataSnapshot
struct Event: Keyed {
var key: String?
let currentEventName: String
let currentEventImage: String
let currentEventPromo: String?
let currentEventDescription: String
//nested properties
let currentEventStreetAddress: String
let currentEventCity: String
let currentEventState: String
let currentEventDate: String?
let currentEventTime: String?
let currentEventEndTime: String?
let currentEventZip: Int
var category: String
//nested properties stop
var currentAttendCount: Int
var isAttending = false
var eventDictionary: [String: Any]{
let dateDict = ["start:date":currentEventDate, "start:time": currentEventTime,"end:time":currentEventEndTime]
return ["event:name":currentEventName,"event:imageURL" : currentEventImage,
"event:description": currentEventDescription, "attend:count": currentAttendCount,
"event:street:address": currentEventStreetAddress,"event:zip": currentEventZip,
"event:state": currentEventState, "event:city": currentEventCity, "event:promo": currentEventPromo ?? "", "event:date": dateDict, "event:category":category]
}
init(currentEventKey: String, dictionary: [String:Any]) {
self.key = currentEventKey
self.currentEventName = dictionary["event:name"] as? String ?? ""
self.currentEventImage = dictionary["event:imageURL"] as? String ?? ""
self.currentEventDescription = dictionary["event:description"] as? String ?? ""
self.currentEventPromo = dictionary["event:promo"] as? String ?? ""
self.currentAttendCount = dictionary["attend:count"] as? Int ?? 0
self.category = dictionary["event:category"] as? String ?? ""
//nested properties
self.currentEventStreetAddress = dictionary["event:street:address"] as? String ?? ""
self.currentEventCity = dictionary["event:city"] as? String ?? ""
self.currentEventState = dictionary["event:state"] as? String ?? ""
self.currentEventZip = dictionary["event:zip"] as? Int ?? 0
let eventDate = dictionary["event:date"] as? [String: Any]
self.currentEventDate = eventDate?["start:date"] as? String ?? ""
self.currentEventTime = eventDate?["start:time"] as? String ?? ""
self.currentEventEndTime = eventDate?["end:time"] as? String ?? ""
}
init?(snapshot: DataSnapshot) {
guard let dict = snapshot.value as? [String : Any],
let currentEventName = dict["event:name"] as? String,
let currentEventImage = dict["event:imageURL"] as? String,
let currentEventDescription = dict["event:description"] as? String,
let currentEventPromo = dict["event:promo"] as? String,
let category = dict["event:category"] as? String,
let currentEventStreetAddress = dict["event:street:address"] as? String,
let currentEventCity = dict["event:city"] as? String,
let currentEventState = dict["event:state"] as? String,
let currentEventZip = dict["event:zip"] as? Int,
let currentAttendCount = dict["attend:count"] as? Int,
let eventDate = dict["event:date"] as? [String: Any],
let currentEventDate = eventDate["start:date"] as? String,
let currentEventTime = eventDate["start:time"] as? String,
let currentEventEndTime = eventDate["end:time"] as? String
else { return nil }
self.key = snapshot.key
self.currentEventName = currentEventName
self.currentEventImage = currentEventImage
self.currentEventDescription = currentEventDescription
self.currentEventStreetAddress = currentEventStreetAddress
self.currentEventCity = currentEventCity
self.currentEventState = currentEventState
self.currentEventZip = currentEventZip
self.currentAttendCount = currentAttendCount
self.currentEventPromo = currentEventPromo
self.currentEventDate = currentEventDate
self.currentEventTime = currentEventTime
self.currentEventEndTime = currentEventEndTime
self.category = category
}
}
Event is a struct, so its instances are passed by value. That is, the call completion(event) passes a copy of event to completion.
Since completion doesn't take the Event as an inout, it is treated as a let, not a var, so you cannot modify it.
You could change the type of completion to #escaping (inout Event?) -> Void. Then the Event will be treated as a var, and changes to it will be effectively copied back out to the caller.
However, that won't make a difference in your case, because you're creating a brand new Event instance for the call to completion (by saying guard let event = Event(snapshot: snapshot)). So even if you let completion modify the Event it receives, you just throw it away after. You also need to write it back to your database somehow after completion returns, if you want the change to isAttending to have any effect.

Firebase fetch user data - Swift

I want to fetch data, I have done this multiple times but for some reason it is not working and I see no reason as to why.
My code for fetching the data
func fetchCurrentUserInfo() {
let currentUser = Auth.auth().currentUser!
let currentUserRef = dataBaseRef.child("users").child(currentUser.uid)
currentUserRef.observeSingleEvent(of: .value, with: { (snapshot) in
let user = UserClass(snapshot: snapshot)
self.downloadImageFromFirebase(urlString: user.photoURL!)
self.userName.text = user.getFullName()
//SD Cache
self.userProfileImage.sd_setImage(with: URL(string: user.photoURL!), placeholderImage: UIImage(named: "UserImageTemplate"))
}) { (error) in
let alert = SCLAlertView()
_ = alert.showError("OOPS!", subTitle: error.localizedDescription)
}
}
My user class file
struct UserClass {
var firstName: String?
var email: String?
var photoURL: String?
var uid: String?
var ref: DatabaseReference!
var key: String?
var lastName: String?
var accountType: String?
init(snapshot: DataSnapshot){
key = snapshot.key
ref = snapshot.ref
accountType = (snapshot.value! as! NSDictionary)["Account Type"] as? String
firstName = (snapshot.value! as! NSDictionary)["First Name"] as? String
email = (snapshot.value! as! NSDictionary)["email"] as? String
uid = (snapshot.value! as! NSDictionary)["uid"] as? String
photoURL = (snapshot.value! as! NSDictionary)["photoURL"] as? String
lastName = (snapshot.value! as! NSDictionary)["Last Name"] as? String
}
func getFullName() -> String {
return ("\(firstName!) \(lastName!)")
}
}
}
I have set the read and write rules to true. When I call the fetchCurrentUserInfo function, it returns all the data for the first name, last name, key, etc. but it returns the photo URL, UID or email as nil giving me the error: fatal error: unexpectedly found nil while unwrapping an Optional value.
Your crash log says it all. You're trying to force unwrap an optional variable by as! casting. Make sure you have valid values in your snapshot.
Also, a better model would like so, this is safer:
struct UserClass {
var firstName: String!
var email: String!
var photoURL: String!
var uid: String!
var ref: DatabaseReference!
var key: String!
var lastName: String!
var accountType: String!
init?(snapshot: DataSnapshot?) {
guard let value = snapshot?.value as? [String: AnyObject],
let accountType = value["Account Type"] as? String,
let firstName = value["First Name"] as? String,
let email = value["email"] as? String,
let uid = value["uid"] as? String,
let photoURL = value["photoURL"] as? String,
let lastName = value["Last Name"] as? String else {
return nil
}
self.key = snapshot?.key
self.ref = snapshot?.ref
self.accountType = accountType
self.firstName = firstName
self.email = email
self.uid = uid
self.photoURL = photoURL
self.lastName = lastName
}
func getFullName() -> String {
return ("\(firstName!) \(lastName!)")
}
}

Could not cast value of type 'NSNull' to 'NSDictionary'

I am getting an error that in the line "self.email = (snapshot.value as! NSDictionary)["email"] as! String" saying that it could not cast value of type 'NSNull'. How do I fix the error? I thought that since I have a function toAnyObject that it would convert everything to a String.
struct User {
var email: String!
var uid: String!
var ref: DatabaseReference!
var key: String = ""
init(snapshot: DataSnapshot) {
self.email = (snapshot.value as! NSDictionary)["email"] as! String
self.uid = (snapshot.value as! NSDictionary)["uid"] as! String
self.ref = snapshot.ref
self.key = snapshot.key
}
init(email: String, uid: String) {
self.email = email
self.uid = uid
self.ref = Database.database().reference()
}
func toAnyObject() -> [String: Any] {
return ["email":self.email, "uid":self.uid]
}
}
As El Captain v2.0 pointed out, don´t force unwrap a value as it might be nil and you´ll get a crash. Use if let instead:
// if snap has a value, then you can use the value. Otherwise not, you can call an else if you want to.
if let snap = snapshot.value as? [String:Any] {
self.email = snap["email"] as! String
}
And I would skip the as! String too and would have done it like this instead, incase of you don´t get the email value.
if let snap = snapshot.value as? [String:Any], let email = snap["email"] as? String {
// Use email in here now
print(email)
}
So if you have other fields you want to get, just add them into the if-statement.
struct User {
var email: String!
var uid: String!
var ref: DatabaseReference!
var key: String = ""
init(snapshot: DataSnapshot){
self.email = (snapshot.value as! NSDictionary)["email"] as! String
self.uid = (snapshot.value as! NSDictionary)["uid"] as! String
self.ref = snapshot.ref
self.key = snapshot.key
}
init(email: String, uid: String){
if let snap = snapshot.value as? [String:Any] {
// Do not unwrap forcefully, instead use as mentioned below
// so that in case snap["email"] gives null that could be
//handled.
//Also please check that whether you have "firstname" var
//present in not in your code and dictionary both.
self.email = snap["email"] as? String ?? "" //Provide some default value or empty string
}
self.uid = uid
self.ref = Database.database().reference()
}
func toAnyObject() -> [String: Any]{
return ["email":self.email, "uid":self.uid]
}
}

Firebase how to get NSDictionary data

I would like to get some data in user profile, I've got some info but I got stuck at this one.
Firebase database:
Here is some of my code:
self.ref.child("users").child("profile").observe(.childAdded, with: { (snapshot) in
if let dictionary = snapshot.value as? [String:Any] {
let user = User()
let brands = dictionary["status"] as! NSDictionary
user.displayname = dictionary["displayname"] as? String
user.isconnected = brands["isconnected"] as? String
print(user.isconnected) //fatal error: unexpectedly found nil while unwrapping an Optional value
class User: NSObject {
var displayname: String?
var isconnected: String?
}
Try something like that :
guard let dictionary = snapshot.value as? [String: Any] else {
return
}
guard let statusDictionary = dictionary["status"] as? [String: Any] else {
return
}
guard let deviceStatusDictionary = statusDictionary["DEVICE_KEY"] as? [String: Any] else {
return
}
let user = User()
user.displayname = dictionary["displayname"] as? String
user.isconnected = deviceStatusDictionary["isconnected"] as? Bool
You need to know your device key.
And :
class User: NSObject {
var displayname: String?
var isconnected: Bool?
}

Resources