I'm a little stuck with something small but that is giving me some headaches! I have a Realtime Database and I am able to retrieve the information I need from it. My only problem is that instead of printing for example (ex.: 200) is printing (ex.: [200])!
This is my code:
func readData() {
FirebaseDatabase.Database.database().reference().child("Available_Funds").observeSingleEvent(of: .value, with: { snapshot in
guard let value = snapshot.value as? [String: Any] else {
return
}
let amountWallet = value.values
print(amountWallet)
self.currentBalanceLabel.text = "$" + "\(amountWallet)"
print("\(value)")
})
}
Right now what I get printed with this code is $[200] for example, instead of just $200, which is what I intend to get.
Tried looking online, but no luck with this! Does someone know how to remove these square brackets from printing?
values is an Array -- thus the []. When you say value.values, you're asking for all of the values of the key/value pairs in snapshot.value.
If you intend to get a single value from it, you would use amountWallet[0] to get the first element. Keep in mind that this will crash if amountWallet has 0 elements (arrays are zero indexed).
amountWallet.first will give you an Optional that will be safe to use, but you would need to unwrap it for printing:
let amountWallet = value.values
if let singleAmount = amountWallet.first {
print(singleAmount)
self.currentBalanceLabel.text = "$" + "\(singleAmount)"
}
You're calling it back as an array of strings [String: Any]
You can either change this (remove []) or access the first element in the array: amountWallet[0].
Related
Looking to retrieve value of custom class from a snap in swift like i do in java , i use Firebasedecoder .
Works fine but i need the following structure
{
username = uiii;
email = test#rom.com
..}
If i make ordered requests like .queryOrdered(ByCHild:email).queryEqual("uiii"), i get the resquest with a previous node :
{
"hjhj"= {
username = uiii;
email = test#rom.com
..} }
Looking for a way to either remove the uneccessary values or to have the correct snap structure.
When you execute a query against the Firebase Database, there will potentially be multiple results. So the snapshot contains a list of those results. Even if there is only a single result, the snapshot will contain a list of one result.
To get to the individual node(s) in the result, you need to loop over snapshot.children, as shown in the Firebase documentation on listening for value events on a list of children.
Also see:
previous questions about looping over children
Get the data from all children in firebase using swift
Firebase queryOrderedByChild() method not giving sorted data for an alternative if you want to only receive a single child node and only once
In short, if you have extra data at the same level and that makes decodeFirebase crash, you still can use it:
let value = snapshot.value
let modifiedValue:NSMutableDictionary = (value as AnyObject).mutableCopy() as! MutableDictionary
You then can remove elements by key: modifiedValue.removeObject(forKey: test)
and then apply decode.
custom class USER with all values in the pictures
import Foundation
import SwiftUI
import Firebase
import CodableFirebase
//knowing the userid , clean beautiful result with Firebasedecoder
func cleanResultWithCodebableFirebase(){
ref.child("3oleg").observeSingleEvent(of: .value, with: { snapshot in
guard let value = snapshot.value else { return }
do {
let user = try FirebaseDecoder().decode(User.self, from: value)
print(user.getUser_id())
} catch let error {
print(error)
}
})
}
not knowing userID dirty result
func customwithdirtylists(){
let query = ref.queryOrdered(byChild: Strings.field_username).queryEqual(toValue: "uiiii")
query.observeSingleEvent(
of: .value, with: { (snapshot) -> Void in
for child in snapshot.children {
let childSnapshot = snapshot.childSnapshot(forPath: (child as AnyObject).key)
for grandchild in childSnapshot.children{
let grandchildSnapshot = childSnapshot.childSnapshot(forPath: (grandchild as AnyObject).key)
//possible from here to get the key and values of each element of the custom class
}
}
})
}
This is the code i use in both cases, direct request or when ordered . No list visible when direct with the help of firebase decode .Ugly way to rebuild custom class thru looping . I m sure there are more elegant ways to do it especially when all i need is just remove one value of the direct result to have a clean result
Please guys i need to parse a string to look like these in swift
"[{"question":9, "answer":25}", "question\":10, "answer":27}]"
where the index and value are dynamically gotten from a loop. I was able to get to these
["{\"question\":9, \"answer\":25}", "{\"question\":10, \"answer\":27}", "{\"question\":11, \"answer\":29}", "{\"question\":12, \"answer\":33}", "{\"question\":13, \"answer\":37}"]
so i have tried this
for i in 0..<answersForQuestionInPage.count{
let questions = answersForQuestionInPage[i] as Answer
do {
let data = try JSONEncoder().encode(questions)
// 2
let string = String(data: data, encoding: .utf8)!
answers.append(string)
print("This is the main value \(string)")
} catch{
}
}
this still gives me an array with this format
["{\"question\":9, \"answer\":25}", "{\"question\":10,
\"answer\":27}", "{\"question\":11, \"answer\":29}",
"{\"question\":12, \"answer\":33}", "{\"question\":13,
\"answer\":37}"]
with the object
"{\"question\":9, \"answer\":25}"
still wrapped in a string liteal " " what i want is for this return array to be in this format
[{"question":9, "answer":25}, {"question":10,
"answer":27}, {"question":11, "answer":29},
"{"question":12, "answer":33}, {"question":13,
"answer":37}]
I didn't understand the whole thing, but you said you need to parse the String, but I think you meant JSON. So, you can do it like this and get the values. Do let me know if it is what you needed, otherwise please add clarity in your question and I will edit and update my answer accordingly.
struct Quiz: Decodable {
let question, answer: Int
}
private func fetchQuizzes() {
//After getting the data from API, you can do this
guard let quiz = try? JSONDecoder().decode([Quiz].self,from: data) else { print("Unable to parse"); return }
print(quiz)
print(quiz.first?.answer) //First Answer
}
Just like Rob said before, you have a JSON here.
Using Robs code you decode the given JSON and create an array of Quiz objects (Robs struct).
You can now work with that array and transform it to your needs.
I'm trying to get my firebase database data into a variable to use it in my project
var someArray = [Array]()
let dbRef = Database.database().reference().child("SomeDatabase")
func loadSomeDatabaseData {
dbRef.observeSingleEvent(of: .value) { (snapshot) in
let someDict = snapshot.value as! [String:Any]
let keysOfSomeDict = Array(someDict.keys)
self.someArray.append(contentsOf: keysOfSomeDict)
self.collectionView?.reloadData()
}
}
I've tried calling loadSomeDatabaseData() in my viewDidload, followed by printing someArray, which results in an empty array. I know the keysOfSomeDict array has the correct data that I want, since i tried printing this array directly inside the closure. I would however also like to be able to print and use this data elsewhere in my app.
The Firebase observeSingleEvent method is asynchronous method. it executive in background only because it take time to fetch data from Firebase.
if you print array immediately means you get only empty array.
so print array once you get the data from Firebase. for that you can use escaping closure
Function Declaration:
func loadSomeDatabaseData(resultArray : #escaping([Array])->()) {
dbRef.observeSingleEvent(of: .value) { (snapshot) in
let someDict = snapshot.value as! [String:Any]
let keysOfSomeDict = Array(someDict.keys)
self.someArray.append(contentsOf: keysOfSomeDict)
resultArray(self.someArray)
}
}
Func Call:
self.loadSomeDatabaseData{(firebaseReposne) in
print("FirebaseData" , firebaseReposne) // Hope here you will get your firebase data.
self.collectionView?.reloadData()
}
The observeSingleEvent is asynchronous. So immediately printing someArray after calling loadSomeDatabaseData will result in an empty array. It takes sometime to retrieve the data from Firebase api.
To use this data elsewhere in the app, you could set a flag indicating the data is loaded or send a notification to inform the data is available.
Okay I am reading from a database and when I print the individual variables they print out correctly. However it seems like the data refuses to append to the array. Anyone know why? I can't figure it out at all.
let commuteBuilder = Commutes()
Database.database().reference().child("Users").child(user).child("Trips").observe(DataEventType.childAdded, with: { (snapshot) in
//print(snapshot)
if let dict = snapshot.value as? NSDictionary {
commuteBuilder.distance = dict["Distance"] as! Double
commuteBuilder.title = dict["TripName"] as! String
commuteBuilder.transportType = (dict["Transport"] as? String)!
}
commuteArray.append(commuteBuilder)
})
print("helper")
print(commuteArray.count)
return commuteArray
The data is correctly added to the array, just not at the time that you print the array's contents.
If you change the code like this, you can see this:
let commuteBuilder = Commutes()
Database.database().reference().child("Users").child(user).child("Trips").observe(DataEventType.childAdded, with: { (snapshot) in
if let dict = snapshot.value as? NSDictionary {
commuteBuilder.distance = dict["Distance"] as! Double
commuteBuilder.title = dict["TripName"] as! String
commuteBuilder.transportType = (dict["Transport"] as? String)!
}
commuteArray.append(commuteBuilder)
print("added one, now have \(commuteArray.count)")
})
print("returning \(commuteArray.count)")
return commuteArray
You'll see it print something like this:
returning 0
added one, now have 1
added one, now have 2
etc.
This is likely not the output you expected. But it is working as intended. Firebase loads data from its database asynchronously. Instead of blocking your code, it lets the thread continue (so the user can continue using the app) and instead calls back to the code block you passed to observe when new data is available.
This means that by the time this code returns the array it is still empty, but it later adds items as they come in. This means that you cannot return data from a function in the way you are trying.
I find it easiest to change my way of thinking about code. Instead of "First get the data, then print it", I frame it as "Start getting the data. When data comes back, print it".
In the code above, I did this by moving the code that prints the count into the callback block. Instead of doing this, you can also create your own callback, which is called a completion handler or closure in Swift. You can find examples in this article, this article, this question Callback function syntax in Swift or of course in Apple's documentation.
I keep getting this error when trying to load an array xputt2 from coreData. When I remove the offending line the data prints perfectly but will not load into the array once I put the line back. Does anyone know what I am doing wrong? Here is the code.
do {
putt2Array = try managedObjectContext.fetch(fetchRequest)
print ("results count is", putt2Array.count)
var i=0
for record in putt2Array {
print(record.value(forKeyPath: "missed")! as! Double)
xputt2[i] = record.value(forKeyPath: "missed")! as! Double
i += 1
}
} catch let error as NSError{
print("Could not fetch data. \(error), \(error.userInfo)")
}
}
The error explains the problem, index is out of range, you are keying into an array at an index that does not yet exist. Try switching
xputt2[i] = record.value(forKeyPath: "missed")! as! Double
to
xputt2.append(record.value(forKeyPath: "missed")! as! Double)
You should be able to remove the i = 0 and incrementing i. On a separate note, I would recommend removing the forced unwraps:
for record in putt2Array {
if let value = record.value(forKeyPath: "missed") as? Double {
xputt2.append(value)
}
}
An array's index only goes as far as the number of elements currently in it. You can use xputt2[i] to replace an element that already exists, but not to add a new one. You can either use xputt2.append to add it to the end, or xputt2.insert if you want to put it at a particular place in the array.
Alternately, since you're just transforming an existing array, you can use the map method to do it in a single line:
xputt2 = putt2array.map{ $0.value(forKeyPath: "missed")! as! Double }