Creating an array out of decoded Firebase data - arrays

I am using the pod CodableFirebase to decode Firebase data and am attempting to place that data into an array. The problem I'm having is its placing each instance of data into a separate array causing me issues when I go to IndexPath it for use in a CollectionView.
The Code:
struct WatchList: Codable {
let filmid: Int?
}
var watchList = [WatchList]()
ref.child("users").child(uid!).child("watchlist").observe(DataEventType.childAdded, with: { (info) in
guard let value = info.value else { return }
do {
let list = try! FirebaseDecoder().decode(WatchList.self, from: value)
self.watchList = [list]
print(self.watchList)
self.watchlistCollection.reloadData()
}
}, withCancel: nil)
Here is how the array is printed to the console:
[Film_Bee.ProfileView.WatchList(filmid: Optional(332562))]
[Film_Bee.ProfileView.WatchList(filmid: Optional(369972))]
[Film_Bee.ProfileView.WatchList(filmid: Optional(335983))]
When I use the array within the CollectionView it only counts and indexpaths the last array.
How can I place the data into a single array?

As commented by #vadian replacing
self.watchList = [list]
with
self.watchList.append(list)
Solved the question.

Related

I want to add the results of a Fetch request from a Core Data entity into an Array

I am using the following code to retrieve rows from an entity in my Core Data DB. I am able to successfully fetch the data. I can also access the rows of the entity and add it to an array. However when I try using the array 'outside' the do { } enclosure, I am only able to read the last array item value. Please assist me.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
guard let appDelegate =
UIApplication.shared.delegate as? AppDelegate else {
return
}
let managedContext =
appDelegate.persistentContainer.viewContext
//2
let fetchRequest = NSFetchRequest<T01_test_results>(entityName: "T01_test_results")
//3
do {
let tests = try managedContext.fetch(fetchRequest)
for test in tests {
testItem.max_number = test.c01_max_number!
testItem.results = test.c01_results!
testItem.test_date = test.c01_test_date!
testItem.timesTable = test.c01_timesTable!
print("In Loop -- \(testItem.timesTable)")
testItem.total_correct = test.c01_total_correct!
testItem.total_questions = test.c01_total_questions!
testArray.append(testItem)
}
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}
print("Single element -> \(testArray[3].timesTable)")
}
Problem seems to be solved. I was using a Class based object to store the retrieved values. Changed this to a struct based type and seems to be working fine!

Swift - Update Value of a specific item in a nested dictionary

I have a tableview and each cells are meant to be linked to an array inside a dictionary.
var buttonAction : [String: [ButtonAction]] = [:]
below is the struct of the buttonAction
struct ButtonAction: Codable {
var action: String
var array_linked_of_buttons: [[String:String]]
init(action: String, array_linked_of_buttons: [[String:String]]) {
self.action = action
self.array_linked_of_buttons = array_linked_of_buttons
}
}
It gets a bit complicated to explain the whole code but when I connect two buttons together, I can get the data for the variable "singleAction" which then can be added to the button action dictionary "array_linked_of_buttons".
let singleAction = [linkedButtonUUID: connectorMarker.UUIDpic.uuidString, linkedButtonCategory: "", linkedButtonName: connectorMarker.name]
let mainMarkerAction = mainMarker.buttonAction["button actions array"]!
for existingMarker in mainMarkerAction {
actionArray.append(existingMarker)
}
var actionSub = actionArray[indexRowTag].array_linked_of_buttons
if let addAction = actionSub.filter({$0[linkedButtonUUID] == connectorMarker.UUIDpic.uuidString}).first {
print("MARKER Exists", addAction)
} else {
actionSub.append(singleAction)
print("UPDATED MARKER", actionSub)
}
let action = ButtonAction(action: actionArray[indexRowTag].action, array_linked_of_buttons: actionSub)
//ISSUE--?? mainMarker.buttonAction.updateValue(VALUE forKey: "button actions array")
saveData()
I can workout which item of the dictionary needs to be edited but how do I update that specific value? I am really confused has it just creates a new item but I want to update a previous one.
So for example, I want to append the "array_linked_buttons" of the item 0 and have 4 items instead of 3.
This is how my dictionary looks like if that helps too
I have searched other questioned but I still work it out.
Any help pointing me in the right direction is much appreciated!
Copying instead of changing value happened because you're using struct for ButtonAction which has value semantics and swift create copy on assigning to any variable.
You need to use class instead. Classes have reference semantics, so it won't create new instance on assigning to variable so you'll be able to update property of needed instance.
I worked out i was updating the main dictionary array and not the nested array.
The code below is what I used in the end.
let singleAction = [linkedButtonUUID: connectorMarker.UUIDpic.uuidString, linkedButtonCategory: "", linkedButtonName: connectorMarker.name]
var mainMarkerAction = mainMarker.buttonAction["button actions array"]!
for existingMarker in mainMarkerAction {
actionArray.append(existingMarker)
}
var actionSub = actionArray[indexRowTag].array_linked_of_buttons
if let addAction = actionSub.filter({$0[linkedButtonUUID] == connectorMarker.UUIDpic.uuidString}).first {
print("MARKER HERE", addAction)
} else {
actionArray[indexRowTag].array_linked_of_buttons.append(singleAction)
}
mainMarkerAction[indexRowTag] = actionArray[indexRowTag]

Add Parse array to dictionary swift

I have some objects in parse and I am getting the data successfully as [PFObjects]. The issue is that I am trying to add the array elements [PFObjects] to a dictionary as values. But I keep getting an empty dictionary, so the values are not added to the dictionary. The dictionary count is also 0.
This is what I tried so far:
var postDictionary = [String:[AnyObject]]()
query.findObjectsInBackground(block: { (posts: [PFObject]?, error:Error?) in
if let unwrappedPosts = posts {
for posts in unwrappedPosts {
if let postText = posts.object(forKey: "title") as?String {
self.titleArray.append(postText)
print("count", self.titleArray.count) // count 10
self.postDictionary["title"]?.append(self.titleArray as AnyObject)
**try to force unwrap **
self.postDictionary["title"]!.append(self.titleArray as AnyObject), and the app crashed
for (title, text) in self.postDictionary {
print("\(title) = \(text)")
}
print("Dictionay text count",self.postDictionary.count) // count is 0
}
}
}
})
This syntax is very confusing
self.titleArray.append(postText)
self.postDictionary["title"]?.append(self.titleArray as AnyObject)
You append a string to an array and then you are going to append the array to the array in the dictionary. I guess this is not intended.
I recommend to map the title strings and set the array for key title once
var postDictionary = [String:[String]]()
query.findObjectsInBackground(block: { (posts: [PFObject]?, error:Error?) in
if let unwrappedPosts = posts {
self.titleArray = unwrappedPosts.compactMap { $0.object(forKey: "title") as? String }
self.postDictionary["title"] = self.titleArray
for (title, text) in self.postDictionary {
print("\(title) = \(text)")
}
print("Dictionay text count",self.postDictionary.count) // count is 0
}
})
Never use AnyObject if the type is more specific.
The proper way of adding to a dictionary is using updateValue because as far as i can see that you don't have the key "title" in your dictionary and you are appending values to unknown key i guess.
This should help:
titleArray.append(postText)
postDictionary.updateValue(titleArray as [AnyObject], forKey: "title")
for (key,value) in postDictionary {
print("\(key) \(value)")
}
Finally this should print:
title [posts1, posts2, posts3]

why are my items not going into the array? Xcode and swift NSURLSession

I am using Swift and Xcode, I have built model object with the following variables:
var itemImageNames: [String]?
var itemTitle: String?
var itemDescription: String?
var itemURL: String?
In the mainviewcontroller, I created an variable of model type. I am initiating a NSURLSession...dataTaskWithURL... and adding itemImageNames that I receive back from the server by using append. The data comes back as valid, I've parsed it and it is indeed coming back as Strings. I've tried two solutions,
create a string array out of the images and set that array to self.item.itemImageNames?
do {
if let json = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? [[String: AnyObject]] {
var imageURLs: [String] = [""]
for dictionary in json {
if let imageURL = dictionary["url"] as? String {
imageURLs.append(imageURL)
print(imageURL)
}
}
self.featuredItem.itemImageNames? = imageURLs
append each of the strings as I get them using self.item.itemImageNames?.append(image)
do {
if let json = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? [[String: AnyObject]] {
for dictionary in json {
if let imageURL = dictionary["url"] as? String {
self.featuredItem.itemImageNames?.append(imageURL)
print(imageURL)
}
}
For some reason, the itemImageNames remains nil, using both approaches. I am sure it will work if I just use one image (e.g. the 1st image), and change itemImageNames to a "String?".
In addition, I can update the itemTitle, itemDescription, and itemURL easily by just setting them to self.item.itemTitle, self.item.itemDescription, self.item.itemURL, respectively. Is there something I'm missing on how to enter information into an array?
In approach #2 initialize the itemImageNames array before trying to append to it. If you try to append to an array that is nil then nothing will happen.
itemImageNames = []
for dictionary in json {
if let imageURL = dictionary["url"] as? String {
self.featuredItem.itemImageNames?.append(imageURL)
print(imageURL)
}
}

How to properly add data from Firebase into Array?

I'm trying to get results from Firebase and put them into Array, but it seems I miss something. What I want is to get 'Time' and 'Blood Glucose" values from Firebase and to put them into an arrays which I will use for Charts. I'm able to put the data into 'BG' and 'TIME' arrays, but when I 'append' them into 'FetchedDate' and 'FetchedBG' I see empty arrays (FetchedBG and FetchedDate)
var FetchedDate:[String]! = []
var FetchedBG: [Double]! = []
//GET DATA FROM FB
func GetDetails(){
let posts = rootRef.child("Diary/\(userID!)/\(passedDATE!)")
//let posts = rootRef.queryOrderedByChild(passedDATE!)
posts.observeEventType(FIRDataEventType.Value , withBlock: { (snapshot) in
for list in snapshot.children {
if let BG = list.value.objectForKey("Blood Glucose")!.doubleValue {
self.FetchedBG.append(BG)
print(BG) // SHOWS RESULTS AS EXPECTED
}
if let TIME = list.value.objectForKey("Time") {
self.FetchedDate.append(TIME as! String)
print(TIME) // SHOWS RESULTS AS EXPECTED
}
}
}) { (error) in
print(error.localizedDescription)
}
}
override func viewDidLoad() {
super.viewDidLoad()
GetDetails()
print(FetchedDate) // EMPTY ARRAY
print(FetchedBG) // EMPTY ARRAY
Firebase loads (and synchronizes) the data from your database asynchronously. Since that may take some time, your code continues executing and you print the arrays while they're still empty.
Once a value is available (either for the first time or when the data has changed), your block is invoked. It adds the data to the arrays. But by that time your print statements have long finished.
The solution is to move the code that needs to run when the value is available (or when it has changed) into the block. E.g.
var FetchedDate:[String]! = []
var FetchedBG: [Double]! = []
//GET DATA FROM FB
func StartSynchronizingDetails(){
let posts = rootRef.child("Diary/\(userID!)/\(passedDATE!)")
//let posts = rootRef.queryOrderedByChild(passedDATE!)
posts.observeEventType(FIRDataEventType.Value , withBlock: { (snapshot) in
for list in snapshot.children {
if let BG = list.value.objectForKey("Blood Glucose")!.doubleValue {
self.FetchedBG.append(BG)
print(BG) // SHOWS RESULTS AS EXPECTED
}
if let TIME = list.value.objectForKey("Time") {
self.FetchedDate.append(TIME as! String)
print(TIME) // SHOWS RESULTS AS EXPECTED
}
}
print(FetchedDate)
print(FetchedBG)
}) { (error) in
print(error.localizedDescription)
}
}
override func viewDidLoad() {
super.viewDidLoad()
StartSynchronizingDetails()
This is a very common pattern when your app interacts with (potentially time-consuming) network resources. It is also precisely the reason Firebase's observeEventType takes a withBlock: argument: to isolate the code that starts synchronizing from the code that responds to value updates.

Resources