I'm using firebase to store the amount of views every video in my app has been seen. What I want to is to gather all views from from one users all videos and display the total number of views. However I'm having problems fetching down the data and putting all the dictionary values together into a Int/String!
Ive tried many different solutions so far, but still I get all the different values in like array / values of the dictionary instead of everything added into one value
This is my code for getting all the videoviews of a specific user, no problems with this so far. When I print "intConvert" I get like all the views in different rows.
let ref = Database.database().reference().child("videoviews").child(stringUid)
ref.observe(.value, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject]{
let numbOfViews = dictionary["views"] as! String
let intConvert = Int(numbOfViews)!
let ArrayViews = [intConvert]
This is my database structure:
**videoviews**
-Lb52VxrEqdSRGljJdP7
views: "25"
-Lb53ARq_lOHEbTruW8s
views: "273"
-Lb53A_cEyX3CYc4mKYn
views: "38"
EDIT: If I do print(dictionary), the dictionary from "if let dictionary = snapshot.value as? [String: Anyobject] looks like this:
["views": 642]
["views": 660]
["views": 628]
["views": 630]
["views": 615]
["views": 3]
["views": 0]
["views": 2]
["views": 1]
Edit: (I was confused and forgot to add the bracelets, sorry for that.)
when I do I "print(dictionary.values) the console looks like this (the key values from different dictionaries):
[642]
[660]
[628]
[630]
[615]
[3]
[0]
[2]
[1]
I then tried to put this together in a loop like this:
var bLoader = 0.0
for hejArray in 0...ArreyViews.count-1{
bLoader += Double(Arrej[hejArray])
}
But when I print "bLoader" I still get all the views for every video in different rows instead of gathered in one value.
So what do I need to do put together all the values from different dictionaries in Firebase into one Variable?
I appreciate all help I can get with this, I can imagine it shouldn't be too hard but that im missing out on something.
EDIT /// I finally found the problem. the "StringUid" that I passed in have different amount of values and therefore the whole function would be called for 9 times if the videos of the user had the amount of 9. The solution that finally worked looked like this:
Global Array declaration:
var myArray = [String]()
Inside the function:
if let dictionary = snapshot.value as? [String: AnyObject]{
let numbOfViews = dictionary["views"] as! String
let intConvert = Int(numbOfViews)!
self.myArray.append(numbOfViews)
let intArray = self.myArray.map { Int($0)!}
let total = intArray.reduce(0, +)
self.totalViewsShower2.text = String(total)
Thank you in advance!
Update
If you can directly print the values from your dictionary like that then the solution might be as easy as
let total = dictionary.values.reduce(0, +)
or if values are strings you need to convert them to Int
let total = dictionary.values.reduce(0) {sum, value -> Int in return sum + (Int(value) ?? 0)}
If on the other hand they values are string but defined as Any you need an extra cast
let total2 = dictionary2.values.reduce(0) {sum, value -> Int in
if let str = value as? String {
return sum + (Int(str) ?? 0)
}
return 0
}
I am not exactly sure what your dictionary contains but I assumed something like this
let dictionary: [String: Any] = ["views": ["1", "2", "3"]]
Then you can cast the value for the "views" key to a String array and then use reduce on that array to get the sum
let array = dictionary["views"] as! [String]
let total = array.reduce(0) {sum, value -> Int in return sum + (Int(value) ?? 0)}
According to your Database structure which looks something like this
{
"videoviews": {
"Lb52VxrEqdSRGljJdP7": {
"views": "25"
},
"Lb53ARq_lOHEbTruW8s": {
"views": "273"
},
"Lb53A_cEyX3CYc4mKYn": {
"views": "38"
}
}
}
let ref = Database.database().reference().child("videoviews").child(stringUid)
ref.observe(.value, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
let viewsArray = dictionary.values
.compactMap { $0 as? [String: String] }
.compactMap { Int($0["views"] ?? "") }
let totalViews = viewsArray.reduce(0, +)
}
})
Related
I'm working on an IOS project that gets its data from Google's Firebase Firestore.
I have Documents like this in Firestore:
5lTSobXhcQBR2oG95s5q
Title: "ABC"
Timestamp: 1554374528.641053
FEeIAlAPlcrVvvtSKn8D
Title: "XYZ"
Timestamp: 1554443702.1300058
In my IOS project I have a Dictionary like this:
myDictionary: [String: [String: Any]] = [5lTSobXhcQBR2oG95s5q: ["Title": "ABC", "Timestamp": 1554374528.641053], FEeIAlAPlcrVvvtSKn8D: ["Title": "XYZ", "Timestamp": 1554443702.1300058]]
How can I sort my Dictionary by Timestamp?
If you are OK with getting back an array of tuples you can apply sorted() with a closure
let sortedTuples = myDictionary.sorted() {
if let t1 = $0.value["Timestamp"] as? Double, let t2 = $1.value["Timestamp"] as? Double {
return t1 < t2
}
return true
}
Note that I by default return true here if either of the values can't be cast to double or doesn't exist. A more advanced logic can of course be implemented depending on which of the two elements fails.
Every collection in iOS or swift has a sort function.
For example:
let sortedArray = dictionary.sort() { return $0.timestamp < $1.timestamp }
$0 will reference to 'the first' element and $1 the element after the first one.
In your example, the logic is the same.
I would do it like this:
Extract the timestamp data and store it in an extra dictionary with the id as a key and the value as a timestamp. Then I would use the .sort function to sort these elements in my dictionary.
I realise that I am missing something simple but as a Swift newbie I am going around in circles & would appreciate a pointer as to what I am doing wrong?!
I have a Core Data Entity called "Numbers" with an attribute (Int16) called "userNumbers". I am fetching the results like:
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Numbers")
//request.predicate = NSPredicate(format: "age = %#", "12")
request.returnsObjectsAsFaults = false
do {
let result = try context.fetch(request)
for data in result as! [NSManagedObject] {
print("\(data.value(forKey: "userNumbers") as! Int16)")
}
} catch {
print("Failed")
}
The result in my console is:
12
13
18
19
21
I need to know how to make this a comma separated list so I can use it in an array. Essentially I need the return to be: 12,13,18,19,21
Everything I try seems to be wrong!
First of all create a more specific fetch request to get a distinct result type
let request = NSFetchRequest<Numbers>(entityName: "Numbers")
A comma separated list is not possible because the type of userNumbers is numeric.
You can map the result to an array of Int16 with
do {
let result = try context.fetch(request) // the type is [Numbers]
let numberArray = result.map{$0.userNumbers}
print(numberArray)
}
I am getting a JSON from an API. So, I want to store a particular value with key SKU. So, what I did was:
var skuArr = [""]
{ (response, error) in
if (error != nil)
{
print("Error \(error.debugDescription)")
}
else
{
self.coinsArr = response.arrayObject as? Array<Dictionary<String, Any>>
for i in 0 ..< (self.coinsArr?.count)!
{
self.skuArr = [response[i]["sku"].rawValue as! String]
}
}
So, with this I am getting the array in skuArr, but when i is 0 I am getting ["a"], and when i is 1 I want it to be ["a","b"], but it gives ["b"] only and when the loop ends with only the last value and not with ["a","b","c","d"] which I want as the final result. How can I insert each of them in the Array?
First of all declare skuArr as empty string array.
var skuArr = [String]()
And this is Swift. There are better ways than ugly index based loops to extract data for example with map or compactMap
if let result = response.arrayObject as? Array<Dictionary<String, Any>> {
self.coinsArr = result
self.skuArr = result.compactMap{ $0["sku"] as? String }
}
And why is coinsArr declared as optional as you are going to force unwrap it anyway? It's highly recommended to use non-optional types as much as possible. Non-optionals can never cause a well-liked unexpected found nil crash
Don't use this:
self.skuArr = [response[i]["sku"].rawValue as! String]
As this will replace the previous value with new one.
Use .append to insert into array.
self.skuArr.append([response[i]["sku"].rawValue as! String])
EDIT
change your initialisation as below:
skuArr: [String] = []
Looking at the structure of my firebase database:
I am attempting to save the values for every key for every cryptocurrency into separate arrays.
For example, the 'coinAmount' key, I would like to get an array looking like ["1.0,"2.0"], and 'coinName' as ["Bitcoin","Ethereum"] etc for each of the 5 keys.
My attempt at this:
let index = NSIndexPath(item: 0, section: 0)
if portfolioCoinFullNameString.count > 0 {
print("PortfolioVC: Number of Coins in Portfolio > 0")
let coinRef = ref.child("\(portfolioCoinFullNameString[index.row])")
coinRef.observe(DataEventType.value) { (snapshot) in
for child in snapshot.children {
let snap = child as! DataSnapshot
let key = snap.key
let value = snap.value
print("key = \(key) ,, value = \(value!)")
if key == "coinAmount" {
self.firebaseCoinAmountArray.append(Double(value as! String)!)
} else if key == "coinName" {
self.firebaseCoinNameArary.append(value as! String)
} else if key == "coinPrice" {
self.firebaseCoinPriceArray.append(Double(value as! String)!)
} else if key == "coinSymbol" {
self.firebaseCoinSymbolArray.append(value as! String)
} else if key == "coinTotalFiat" {
self.firebaseCoinTotalFiatArray.append(Double(value as! String)!)
} else {
print("PortfolioVC: Key does not match anything!")
}
}
}
} else {
print("PortfolioVC: Number of Coins in Portfolio !> 0")
}
This works for adding the first coin, but when i attempt to add the second one the 2 values are appended to the array so it contains 3, and if i add a third coin it append 3 values. Not sure why its looping through each coin?
EDIT:
I searched around StackOverflow and came across another method:
let index = NSIndexPath(item: 0, section: 0)
if portfolioCoinFullNameString.count > 0 {
print("PortfolioVC: Number of Coins in Portfolio > 0")
let coinRef = ref.child("\(portfolioCoinFullNameString[index.row])")
coinRef.observe(DataEventType.value) { (snapshot) in
let value = snapshot.value as? NSDictionary
let coinName = value?["coinName"] as? String ?? ""
self.firebaseCoinNameArary.append(coinName)
print("aaaaaa:\(self.firebaseCoinNameArary)")
}
} else {
print("PortfolioVC: Number of Coins in Portfolio !> 0")
}
But still have the issue of when adding any coin as second element in the array, the array contains 3x first coin added instead of just two entires of the first coin and second coin. I know i could do an if statement to check if the array already contains the name and if it does dont add it, but how would i do this with the number values as theres the possibility coins could have the same price.??
Edit2: Been thinking about it, would it work if i just observe the values and not for changes, save all of the to Arrays, populate the cells with them. Then if a user edits the 'coinAmount' value in the app, it updates in Firebase, but not the arrays. Then, upon user closing app/logging out, next time portfolio is shown it will re-pull the values from firebase which contains the updated numbers?
Edit3: Also, the nodes under portfolio called 'Bitcoin' etc, if i called them coin1, coin2, coin3 etc, how would i get that path and get the key and values for say coin3? Is there a way to just get the values for All the nodes under 'protfolio' in one go?
The idea is once I save all the firebase data to the arrays i will populate a tableview cell with all of the data.
Any help is appreciated :)
Thanks,
Jeremy
In order to do this don't use a for-loop use .childAdded
I will provide an example
database.ADD THE ENTIRE PATH.child("portfolio").observe(DataEventType.childadded) { (snapshot) in
let key = snapshot.key
let value = snapsnapshot.value as? NSDictionary
//Add everything that is in your firebase
let coinAmount = value?["coinAmount"] as? String
let cointName = value?["coinName"] as? String
//.... Added all the variables for the database
//use the variables above and append the array and it will work!
}
}
I prepare a swift Array in my Watch Interface and send it to the iOS App:
#IBAction func buttonGeklickt() {
if WCSession.isSupported() {
let session = WCSession.defaultSession()
session.delegate = self
session.activateSession()
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "hh:mm"
let datumString = dateFormatter.stringFromDate(NSDate())
var swiftArray = [String]()
swiftArray.append(datumString)
var swiftDict = ["a":swiftArray]
session.transferUserInfo(swiftDict)
}
so far so good, on the iOS App the dictionary arrives, but there seems to be something wrong with the Array in the Dictionary:
func session(session: WCSession, didReceiveUserInfo userInfo: [String : AnyObject]) {
print ("seems to be the same Dict = \(userInfo)")
if let vw = userInfo["a"] as? [String: String] {
print ("Never called! Here I would expect my array from the watch \(vw)")
}
}
I would expect and like vw to hold the same array as swiftArray in the watchApp. However it seems to be of type __NSCFArray:
screenshot
So what I'm doing wrong here?
I'm new to Swift, however I'm experienced with Objective C to solve actually every problem I faced in the past years, but this issue seems to be so basic and it's embarrassing that I'm not able to solve it on my own. So help is much appreciated
If I understand your code correctly, you are saving "a" as value of type [String]. But you are trying to read it as [String:String]. Instead of
if let vw = userInfo["a"] as? [String: String]
try
if let vw = userInfo["a"] as? [String]