Up until two days ago my code was working fine with no problems, out of the blue my code begins returning nil when I know for a fact that the value is there within my Firebase node. I have not touched the code in weeks nor have made anychanges to it any time recently. I have recently upgraded my Xcode to 9 but still running Swift 3.
I have the value radiusDistanceNumber declared above my viewDidLoad() as
class viewController: UIViewController {
var radiusDistanceNumber: Int()
override func viewDidLoad()
super.viewDidLoad {
}
func radiusValue(){
let user = Auth.auth().currentUser
guard let uid = user?.uid else{
return
}
let ref = Database.database().reference().child("Users").child(uid)
ref.observeSingleEvent(of: .value, with: {snapshot in
print("this is the snapshot value \(snapshot.value)")
//returns correct value of 14
if let dictionary = snapshot.value as? [String: AnyObject] {
self.radiusDistanceNumber = dictionary["radiusDistance"] as? Int
print(self.radiusDistanceNumber)
//returns nil
if self.radiusDistanceNumber == nil {
//let the user know it may be an error in connection
let alertController = UIAlertController(
title: "Error",
message: "Data not loading properly, make sure you have a strong connection and try again", preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "Got it", style: .cancel, handler: nil)
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)
} else{
// pass the value to the slider so the user can see the distance
let radiusDistanceNumberFloat = Float(self.radiusDistanceNumber!)
self.radiusSlider.value = radiusDistanceNumberFloat
self.radiusLabel.text = String(self.radiusSlider.value)
}
}
})
}
Again, this code was working weeks ago
I think you should make these changes in your code . You are currently declaring the radiusDistanceNumber incorrectly so
Replace
var radiusDistanceNumber: Int()
with
var radiusDistanceNumber = Int()
I think you should also
replace
if let dictionary = snapshot.value as? [String: AnyObject]
with
if let dictionary = snapshot.value as? [String: Any]
Related
func teacherExists(teacherName: String) -> Bool
{
var dataBaseRef2: DatabaseReference!
dataBaseRef2 = Database.database().reference()
let teachersTableRef = dataBaseRef2.child("teachers")
self.teachersList.removeAll()
teachersTableRef.observeSingleEvent(of: DataEventType.value, with: { (snapshot) in
// teachersTableRef.observe(.value)
//{
// snapshot in
let teachersNamesDictionary = snapshot.value as? [String: Any] ?? [:]
for(key, _) in teachersNamesDictionary
{
if let teacherDict = teachersNamesDictionary[key] as? [String: Any]
{
if let teacher = Teacher(dictionary: teacherDict)
{
//print(teacher.teacher_name)
self.teachersList.append(teacher.teacher_name)
}
}
}
print(self.teachersList.count)
})
print("Outside \(self.teachersList)")
return false
}
Because Firebase APIs are all asynchronous. It would be bad for your app if they blocked your code path, because that could cause your app to hang indefinitely.
observeSingleEvent returns immediately, and the passed observer gets invoked some time later, whenever the data is finally ready. Execution continues on the next line, which prints to the console.
getting error upon calling teacherExists function
let OKAction = UIAlertAction(title: "OK", style: .default, handler:
{
(action: UIAlertAction!) ->Void in
let textfield = alert.textFields![0] as UITextField
newTeacherName = textfield.text!.uppercased()
if !(newTeacherName.isEmpty)
{
//checking if teacher already exists using function teacherExists
let exists = self.teacherExists(teacherName: newTeacherName, completion:
if exists == true //if duplicate teacher is found
{
let alert = UIAlertController(title: "Duplicate Teacher", message: "Teacher \(newTeacherName) has been added earlier", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
else
{
//add teacher to database here
let dict = ["teacher_name" : newTeacherName]
let newTeacher = Teacher(dictionary: dict)
let tableRef = self.dataBaseRef.child("teachers") //getting reference of node with name teachers
let recordRef = tableRef.childByAutoId() //creating a new record in teachers node
recordRef.setValue(newTeacher!.toDictionary())//adding data to new record in teachers node
}
}
})
You can use closure to callback after check for duplicate
func teacherExists(teacherName: String, completion: #escaping ((Bool) -> Void)) -> Void {
var dataBaseRef2: DatabaseReference!
dataBaseRef2 = Database.database().reference()
let teachersTableRef = dataBaseRef2.child("teachers")
self.teachersList.removeAll()
teachersTableRef.observeSingleEvent(of: DataEventType.value, with: { (snapshot) in
let teachersNamesDictionary = snapshot.value as? [String: Any] ?? [:]
for(key, _) in teachersNamesDictionary
{
if let teacherDict = teachersNamesDictionary[key] as? [String: Any]
{
if let teacher = Teacher(dictionary: teacherDict)
{
//print(teacher.teacher_name)
self.teachersList.append(teacher.teacher_name)
}
}
}
let exists = self.teachersList.contains(teacherName)
completion(exists)
})
}
And call function as below
teacherExists(teacherName: newTeacherName) { (exists) in
if exists {
// show alert
} else {
// add new teacher to db
}
}
Hope it help!
I run a python script on my raspberry pi that takes a picture, moves it in the cloud and uploads the link to firebase.
Because the picture taking and uploading takes approximately 15 seconds I'm using DispatchQueue.main.asyncAfter.
In my current state, I'm able to take pictures whenever I want to, but there are 2 things I can't do.
How to be able to get the firebase element whenever a change happened. Python can do that, I don't know what the swift method for that.
The picture doesn't show. Is it because I want to display it from DispatchQueue.main.asyncAfter?
Thanks
import UIKit
import Firebase
import FirebaseDatabase
class RpiOps: UIViewController {
#IBOutlet weak var ivImage: UIImageView!
#IBOutlet weak var LblResult: UILabel!
#IBOutlet weak var tvLink: UITextView!
#IBAction func btn1Pic(_ sender: Any) {
rpi2do(state: "single_pic")
rpiResults(state: "-")
let actityIndicator = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
actityIndicator.center = self.view.center
actityIndicator.hidesWhenStopped = true
actityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.gray
view.addSubview(actityIndicator)
actityIndicator.startAnimating()
UIApplication.shared.beginIgnoringInteractionEvents()
DispatchQueue.main.asyncAfter(deadline: .now() + 20) {
actityIndicator.stopAnimating()
UIApplication.shared.endIgnoringInteractionEvents()
let ref = Database.database().reference()
//let post : [String: AnyObject] = ["2do": state as AnyObject]
ref.child("rpi_results").observeSingleEvent(of: .value, with: { (snapshot) in
let ud = snapshot.value as! [String: Any]
let asa = ud["got"] as! String
self.tvLink.text = asa
print("asa:", asa)
//self.LblResult
let url = URL(string: asa)
let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
self.ivImage.image = UIImage(data: data!)
self.displayAlert(title: "Finished!", message: "Your picture has been taken. See the link here: " + asa)
//self.rpi2do(state: "-")
})
}
rpi2do(state: "-")
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
// rpi operation
func rpi2do(state: String) {
let ref = Database.database().reference()
let post : [String: AnyObject] = ["2do": state as AnyObject]
ref.child("rpi2do").setValue(post)
}
// rpi operation
func rpiResults(state: String) {
let ref = Database.database().reference()
let post : [String: AnyObject] = ["got": state as AnyObject]
ref.child("rpi_results").setValue(post)
}
func displayAlert(title: String, message: String) {
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (action) in
self.dismiss(animated: true, completion: nil)
}))
self.present(alert, animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
According to the Firebase docs, I think you've got the right method above in observeSingleElement to answer your first question. On the second one, make sure that you are getting the results of observeSingleEvent back onto the main thread before trying updating any UI elements, like below. You may be seeing the no change in the photo because you are not on the main thread after the database call.
ref.child("rpi_results").observeSingleEvent(of: .value, with: { (snapshot) in
let ud = snapshot.value as! [String: Any]
let asa = ud["got"] as! String
DispatchQueue.main.sync {
self.tvLink.text = asa
print("asa:", asa)
//self.LblResult
let url = URL(string: asa)
let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
self.ivImage.image = UIImage(data: data!)
self.displayAlert(title: "Finished!", message: "Your picture has been taken. See the link here: " + asa)
//self.rpi2do(state: "-")
}
})
Everything was working fine, then I deleted some old messages and conversations from My Firebase Database. Now every time I send a message I get a crash. I deleted all old users and created new users and tried to send messages and I still keep getting a crash. I am not sure what can be causing this. Any suggestions will be helpful. It first happened after I tested out this function to delete the table cell...
func deleteConversation(_ conversation:Conversation) {
guard let user = Auth.auth().currentUser else { return }
let ref = Database.database().reference()
let obj = [
"conversations/users/\(user.uid)/\(conversation.partner_uid)/muted": true
] as [String:Any]
print("OBBJ: \(obj)")
ref.updateChildValues(obj, withCompletionBlock: { error, ref in
if error != nil {
let alert = UIAlertController(title: "Error deleting conversation!", message: nil, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Okay", style: .default, handler: nil))
} else {
let alert = UIAlertController(title: "Conversation deleted!", message: nil, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Okay", style: .default, handler: nil))
}
})
}
func downloadMessages() {
self.messages = []
downloadRef?.observe(.childAdded, with: { snapshot in
let dict = snapshot.value as! [String:AnyObject]
if let sender = dict["sender"] as! String!, let recipient = dict["recipient"] as! String!, let text = dict["text"] as! String!, text.characters.count > 0 {
let timestamp = dict["timestamp"] as! Double
let date = NSDate(timeIntervalSince1970: timestamp/1000)
let message = JSQMessage(senderId: sender, senderDisplayName: "", date: date as Date!, text: text)
self.messages.append(message!)
self.reloadMessagesView()
self.finishReceivingMessage(animated: true)
}
else if let id = dict["sender"] as! String!,
let photoURL = dict["imageUrl"] as! String!, photoURL.characters.count > 0 { // 1
// 2
if let mediaItem = JSQPhotoMediaItem(maskAsOutgoing: id == self.senderId) {
// 3
let timestamp = dict["timestamp"] as! Double
let date = NSDate(timeIntervalSince1970: timestamp/1000)
if let message = JSQMessage(senderId: id, senderDisplayName: "", date: date as Date!, media: mediaItem) {
self.messages.append(message)
if (mediaItem.image == nil) {
self.photoMessageMap[snapshot.key] = mediaItem
}
self.collectionView.reloadData()
}
if photoURL.hasPrefix("gs://") {
self.fetchImageDataAtURL(photoURL, forMediaItem: mediaItem, clearsPhotoMessageMapOnSuccessForKey: nil)
}
}
}
else {
print("Error! Could not decode message data")
}
})
// We can also use the observer method to listen for
// changes to existing messages.
// We use this to be notified when a photo has been stored
// to the Firebase Storage, so we can update the message data
updatedMessageRefHandle = downloadRef?.observe(.childChanged, with: { (snapshot) in
let key = snapshot.key
let messageData = snapshot.value as! Dictionary<String, String> // 1
if let photoURL = messageData["imageUrl"] as String! { // 2
// The photo has been updated.
if let mediaItem = self.photoMessageMap[key] { // 3
self.fetchImageDataAtURL(photoURL, forMediaItem: mediaItem, clearsPhotoMessageMapOnSuccessForKey: key) // 4
}
}
})
}
It's very likely the error is a result of force casting - as!
Instead of
let messageData = snapshot.value as! Dictionary<String, String>
do
guard let messageData = snapshot.value as? Dictionary<String, String> else { return }
Your snapshot.value is either nil, or is not an instance of Dictionary<String, String>, and force casting it to such will result in crash.
You should also read more about optionals and type casting in Swift, because you use ! a lot, and not once in your program is it used correctly.
For some reason when I am adding data to Firebase database through a form on my app it saves the data to the database but three times instead of just once as it's supposed to.
I can't quite figure out why because I have used this code before and it has worked fine...
Code:
#IBAction func createPostTapped(_ sender: UIButton) {
if let uid = Auth.auth().currentUser?.uid {
Database.database().reference().child("users").child(uid).observeSingleEvent(of: .value, with: {
(snapshot) in
if let userDictionary = snapshot.value as? [String: AnyObject] {
for user in userDictionary {
if let username = user.value as? String {
if let game = self.gameTextField.text {
if let activity = self.activityTextField.text {
if let console = self.consoleTextField.text {
if let skill = self.skillTextField.text {
if let communication = self.communicationTextField.text {
if let lfglfm = self.lfglfmTextField.text {
if let description = self.descriptionTextView.text {
let postObject: Dictionary<String, Any> = [
"uid" : uid,
"username" : username,
"game" : game,
"activity" : activity,
"console" : console,
"skill" : skill,
"communication" : communication,
"lfglfm" : lfglfm,
"description" : description
]
Database.database().reference().child("posts").childByAutoId().setValue(postObject)
let alert = UIAlertController(title: "Success!", message: "Your post was added successfully.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) in
//code will run when ok button is pressed
let vc = self.storyboard?.instantiateViewController(withIdentifier: "LoggedInVC")
self.present(vc!, animated: true, completion: nil)
}))
self.present(alert, animated: true, completion: nil)
}
}
}
}
}
}
}
}
}
}
})
}
}
If anyone has any idea why my code would be posting the data three times instead of once I would appreciate the help.
Thank you!
I am guessing it is this line of code:
for user in userDictionary {
Looks like you have 3 entries inside that node so
Database.database().reference().child("posts").childByAutoId().setValue(postObject)
actually executes 3 times.
As the title says, I'm working on creating an app that stores user data and then displays that data at the app's "homescreen" every time you open the app. I've started off with just the username, but I can't seem to get it to work.
When running the app, it never encounters any errors. However after hitting the "Save" button, the label's text is not updated to show the name that was entered. I'm so confused! :/ Would anyone mind taking a quick look if you can?
EDIT: Updated the code. Now I'm getting an error in the "updateName" function. On the line "var person = people[0]" I get the following: "Fatal error: Array index out of range." I don't get why the 0 index wasn't already set as the alert's textfield. Any ideas?
class InitialViewController: UIViewController, UITextFieldDelegate {
var didRunBefore: Bool = NSUserDefaults.standardUserDefaults().boolForKey("didRunBefore")
#IBOutlet weak var userGreeting: UILabel!
var people = [NSManagedObject]()
var alert = UIAlertController(title: "Hello!", message: "What's your name?", preferredStyle: .Alert)
override func viewDidAppear(animated: Bool) {
if !didRunBefore {
getUserName()
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "didRunBefore")
NSUserDefaults.standardUserDefaults().synchronize()
}
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext!
let fetchRequest = NSFetchRequest(entityName:"Person")
var error: NSError?
let fetchedResults = managedContext.executeFetchRequest(fetchRequest, error: &error) as? [NSManagedObject]
if let results = fetchedResults {
people = results
} else {
println("Could not fetch \(error), \(error!.userInfo)")
}
}
func getUserName() {
userGreeting.text = ""
let saveAction = UIAlertAction(title: "Save", style: .Default) { (action: UIAlertAction!) -> Void in
let textField = self.alert.textFields![0] as! UITextField
self.saveName(textField.text)
}
let cancelAction = UIAlertAction(title: "Cancel", style: .Default) { (action: UIAlertAction!) -> Void in
}
alert.addTextFieldWithConfigurationHandler {
(textField: UITextField!) -> Void in
}
alert.addAction(saveAction)
alert.addAction(cancelAction)
func updateName() {
var person = people[0]
if let userName1 = person.valueForKey("name") as? String {
userGreeting.text = "Hey there, \(userName1)!"
}
}
presentViewController(alert, animated: true, completion: updateName)
}
func saveName(name: String) {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext!
let entity1 = NSEntityDescription.entityForName("Person", inManagedObjectContext: managedContext)
let person = NSManagedObject(entity: entity1!, insertIntoManagedObjectContext: managedContext)
person.setValue(name, forKey: "name")
var error: NSError?
if !managedContext.save(&error) {
println("Could not save \(error), \(error?.userInfo)")
}
people[0] = person
}
}
In order to update a UILabel, you have to assign a string to its .text property. In this case, you have a UILabel called userGreeting. You never have assigned its text to a value (although I see you reference it twice) , and thus the label will not update.
For example, this would set the label to the name Bill
userGreeting.text = "Bill"
Replace "Bill" with your custom name to get your desired result.