How can you combine two firebase observation results into the same array - arrays

I have two nodes in my firebase DB that tracks like and comment notifications. Each value is parsed as a Notification class and I am looking to be able to get the snapshots from each node and create a merged notification array. The two functions I have work to pull the respective data but I dont know how to merge the two arrays. I tried to use sets and then form a union but I cant do it with non hashable values. The only other solution I could think of was to combine the two functions into one, however, this seemed and looked very wrong. Here is what Im working with...
var commentNotifications = [Notifications]()
func commentNotificationObserver(_ update: #escaping () -> Void) {
commentHandle = CURRENT_USER_COMMENT_NOTIFICATION.observe(DataEventType.value, with: { (snapshot) in
self.commentNotifications.removeAll()
for child in snapshot.children.allObjects as! [DataSnapshot] {
let id = child.key
let dict = child.value as! Dictionary<String, Any>
let notif: Notifications = Notifications(notifID: id, data: dict)
self.commentNotifications.insert(notif, at: 0)
update()
}
if snapshot.childrenCount == 0 {
update()
}
})
}
// Detects New Likes
var notifications = [Notifications]()
func likeNotificationObserver(_ update: #escaping () -> Void) {
likesHandle = CURRENT_USER_LIKE_NOTIFICATION.observe(DataEventType.value, with: { (snapshot) in
self.notifications.removeAll()
for child in snapshot.children.allObjects as! [DataSnapshot] {
for dictionary in child.value as! Dictionary<String, Any> {
let id = dictionary.key
let dict = dictionary.value as! Dictionary<String, Any>
let notif: Notifications = Notifications(notifID: id, data: dict)
self.notifications.insert(notif, at: 0)
update()
}
}
if snapshot.childrenCount == 0 {
update()
}
})
}
So as you can see I have a notifications array and a commentNotification array. How can I get these into the same array without duplicates. Thanks!

Related

TableView is not populating after populated from loaded array

I have read through the similar questions, but am still stuck.
I have an array in a separate swift file called getSkills. It connects to a Firebase database and returns the skills into a variable/array called mySkills.
func getSkills(owner: String) {
mySkills = []
let db = Firestore.firestore()
db.collection("Skills").order(by: "SkillName").getDocuments { (querySnapshot, error) in
if let e = error {
print(e)
//TO DO: show error on screen
} else {
if let safeDocs = querySnapshot?.documents {
for doc in safeDocs {
let data = doc.data()
if let sklName = data["SkillName"] as? String,
let sklType = data["SkillType"] as? String,
let sklLevel = data["SkillLevel"] as? String,
let sklIsTimed = data["isTimed"] as? Bool,
let sklSecs : Int = data["Seconds"] as? Int,
let sklOwner = data["Owner"] as? String,
let sklID = doc.documentID as? String {
let newSkill = Skill(id: sklID, name: sklName, type: sklType, isTimed: sklIsTimed, seconds: sklSecs, level: sklLevel, owner: sklOwner)
mySkills.append(newSkill)
}
}
}
}
}
}
In a separate swift view controller, I am calling the getSkills function in ViewDidLoad.
override func viewDidLoad() {
super.viewDidLoad()
skillList.dataSource = self
skillList.delegate = self
DispatchQueue.main.async {
getSkills(owner: getUserID())
print(mySkills.count)
self.skillList.reloadData()
}
However, when the View Controller displays in the simulator the tableview is not populated with the list of Skills.
I can get the issue to work with a "LoadData()" function in the View Controller. However, I'm trying to build a reusable getSkills function that I can call in multiple locations as opposed to writing the same code over and over.
I've tried the following things:
Dispatch Main Queue in the getSkills function
Dispatch main queue in the ViewController itself
Moving the Dispatch main queue lines to the ViewWillAppear function on the ViewController.
wrapping the mySkills.append in the getSkills function within Dispatch Main Queue
Any ideas?

Reading an array in Firebase

I need some help in reading the "users" array in my Firebase structure as shown below.
I use the below function to grab the data from the channels table.
func observeChannels(channelId: String, onSuccess: #escaping (ChannelModel) -> Void) {
DB_REF_CHANNELS.child(channelId).observeSingleEvent(of: .value) { (snapshot) in
if let dict = snapshot.value as? [String: Any] {
let channel = ChannelModel.transformChannel(dict: dict, key: snapshot.key)
onSuccess(channel)
}
}
}
This is then passed to the ChannelModel class so I can grab each individual data.
class ChannelModel {
var channelId: String?
var channelName: String?
var ownerId: String?
var users: Dictionary<String, Any>? // Array of users }
extension ChannelModel {
static func transformChannel(dict: [String: Any], key: String) -> ChannelModel {
let channel = ChannelModel()
channel.channelId = key
channel.channelName = dict["channelName"] as? String
channel.ownerId = dict["ownerId"] as? String
channel.users = dict["users"] as? Dictionary<String, Any>
return channel
}}
The observeChannels function is then called which returns me an object of all the channels.
observeChannels(channelId: channelId, onSuccess: { (channel) in
self.channels.insert(channel, at: 0)
self.tableView.reloadData()
let user = channel.users!
print(user)
})
When I run the above function to get the users, my output is as follows:
["pZaEJ5aAAkR7WzgIJ4Wqf10jXot1": 1, "asdasldljAlsjkasldj": 1]
I was wondering how would I just get the key value - i.e:
["pZaEJ5aAAkR7WzgIJ4Wqf10jXot1", "asdasldljAlsjkasldj"]
Thanks in advance!
Try this:
let usersDictionary = channel.users!
usersArray = Array(usersDictionary.keys)
print(usersArray)
instead of:
let user = channel.users!
print(user)

Cannot invoke index with an argument list of type '(of: Any)'

I am making a news app where you can select topics that you want to see. The problem I am having is where you deselect the topic. All of the selected topics are added to CoreData in an Entity called ArticleSource under the Attribute of source. The error occurs when I try to locate the topic in the array called Results using the string title. As I dont know the position of the topic in the array I try to locate it using index(of: ) method which produces the error: Cannot invoke index with an argument list of type '(of: Any)'
Any help appreciated.
do {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "ArticleSource")
request.returnsObjectsAsFaults = false
var results = try context.fetch(request)
if results.count > 0 {
for result in results as! [NSManagedObject] {
if let source = result.value(forKey: "source") as? String {
if source == title {
print("it matches")
if let index = results.index(of: title) {
results.remove(at: index)
}
}
print("results = \(results)")
}
}
}
} catch {
print("error")
}
do {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
try context.save()
print("SAVED")
} catch {
// error
}
There is no need to loop through results to get the index. You can try this.
var results = context.fetch(request)
if let index = results.index(where: { (result) -> Bool in
result.value(forKey: "source") == title
})
{
results.remove(at: index)
}
A likely cause of Cannot invoke index with an argument list of type '(of: X)
is because the type X does not conform to Equatable
In arr.index(of: <Element>), Element should conform to Equatable, and type X does not conform to Equatable:
For an array of [X], use arr.index(where:)
Update your code as:
if let index = results.index(where: { $0 as? String == title }) {
print(index)
}

Swift 3 - Pass Dictionary inside Array from JSON to Another Method

I have the following JSON:
{
"stores":2,
"store_data":[
{
"store_id":1,
"store_name":"Target"
"store_color":"000000"
},
{
"store_id":2,
"store_name":"Walmart"
"store_color":"FFFFFF"
}
]
}
And I am collecting it (within a function) the following way (safeguards removed for simplicity):
let task = URLSession.shared.dataTask(with: baseURL) { (data, response, error) in
if let tmpRawData: NSDictionary = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSDictionary {
process(rawData: tmpRawData)
}
}
And sending it to the helper process function defined as:
func process(rawData: NSDictionary) -> Bool {
if let storeDataArray = rawData["store_data"] {
// Here I want to loop through the store_data array
}
}
And I am having some trouble looping through the array in the function above. Initially, I had tried:
for store: Dictionary<String, String> in storeDataArray {
// Do things with store["store_id"] here
}
But I am new to swift and am having trouble deciphering between NSArray, Array, Dictionary, NSDictionary, etc. I'm working in Swift 3. Any help is much appreciated!
First of all, don't annotate types that much. The compiler will tell you if it needs an explicit annotation
Second of all it's convenient to declare a type alias for a JSON dictionary
typealias JSONObject = [String:Any]
This is the task, tmpRawData is a dictionary – represented by {} in the JSON.
let task = URLSession.shared.dataTask(with: baseURL) { (data, response, error) in
if let tmpRawData = try JSONSerialization.jsonObject(with: data, options: []) as! JSONObject {
process(rawData: tmpRawData)
}
}
and the process function, the type alias makes everything more readable.
The value of rawData is an array of dictionaries – represented by [] in the JSON.
func process(rawData: JSONObject) -> Bool {
if let storeDataArray = rawData["store_data"] as? [JSONObject] {
for store in storeDataArray {
let storeID = store["store_id"] as! Int
let storeName = store["store_name"] as! String
let storeColor = store["store_color"] as! String
print(storeID, storeName, storeColor)
}
}
}
I have no idea why all tutorials suggests the mutableContainers option. You don't need it at all in Swift when using native collection types.

Querying users in Firebase with unique UID's as keys using Swift

I'm trying to query users by lowercaseString name which I auto save when a user signs up. The thing is I'm creating keys for users using their uid's. Now I'm having trouble letting other users find their friends because all I'm pulling back is the very first key "user". Here's my firebase info...
Here is my code...
let userRef = self.ref.childByAppendingPath("users")
userRef.queryOrderedByChild("caseUsername")
.queryStartingAtValue(self.searchTXTFLD.text?.lowercaseString, childKey: "caseUsername")
.observeEventType(.Value, withBlock: { (snapshot) -> Void in
print(snapshot.key)
Please help me find users using self.searchTXTFLD.text?.lowercaseString. It should equal user child caseUsername.
Thanks to #Frank van Puffelen. The answer illuminated more about how Firebase data should be structured. I didn't completely use your answer, but here is what I have and what works.
#IBAction func searchFriendsACTN(sender: AnyObject)
{
let queryRef = self.ref.childByAppendingPath("users")
queryRef.queryOrderedByChild("caseUsername")
.queryEqualToValue(self.searchTXTFLD.text?.lowercaseString)
.queryLimitedToFirst(1)
.observeEventType(.Value, withBlock: { snapshot in
if snapshot.exists()
{
var newIds = [String]()
var newNames = [String]()
var newCsnames = [String]()
var newPics = [String]()
for items in snapshot.value as! NSDictionary
{
let ids = items.key
let names = items.value["displayName"] as! String
let csnames = items.value["caseUsername"] as! String
let pics = items.value["image"] as! NSString
newIds.append(ids as! String)
newNames.append(names)
newCsnames.append(csnames)
newPics.append(pics as String)
}
self.caseUserNameArray = newCsnames
self.usersID = newIds
self.usernameArray = newNames
self.photos = newPics
self.friendsTBLVW.reloadData()
}
})
}
When you have a query, there may be multiple children that match the condition. When you then listen for the .Value event, Firebase will return a snapshot that contains a list of the matching children. Even if there is only one matching child, it will still be in a list.
So the solution is to iterate over the matching children:
ref.queryOrderedByChild("caseUsername")
.queryStartingAtValue(self.searchTXTFLD.text?.lowercaseString, childKey: "caseUsername")
.observeEventType(.Value, withBlock: { snapshot in
for child in snapshot.children {
print(child.key);
}
});

Resources