Cross reference columns in Parse to decide which users to Display on a view controller. Swift, user search settings - ios

What I am trying to figure out is how to only display users that meet the setting requirements that the user previously saved on a different ViewController. Ok so on set up the current user has selected their genre and instrument and it has saved in columns in Parse called "genre" and "instrument" as Strings. Then on the search settings page the user has selected that they would like to search for lets say "Rock" as the genre and "Acoustic guitar" as the instrument. Both of these then get added to Parse under the columns "genreSearch" and "instrumentSearch".
So I know I need to make a query and display it on the ViewController that the users are displayed on but I don't know how. I am trying to basically cross reference the column "genre" of other users against the current users column "genreSearch". I imagined it would be something like this:
genreQuery.whereKey("username",notEqualTo:PFUser.currentUser()!.username!)
genreQuery.whereKey("genre", notEqualTo:PFUser.currentUser()!username!)
genreQuery.whereKey("genreSearch", equalTo:PFUser.currentUser()!)
genreQuery.findObjectsInBackgroundWithBlock { (users: [AnyObject]?,
error: NSError?) -> Void in
if error == nil {
for user in users! {
if self.genre == self.genreSearch {
print("These two strings are considered equal")
appUsers.append(user as! PFUser)
}
self.resultsPageTableView.reloadData()`
At the top of my VC I have as I am storing and displaying all the users in a cell which also links to another VC to show more details.
var genre = [String]()
var genreSearch = [String]()
var appUsers = [PFUser]()
I have read Parse docs and to be honest now I am more confused as where to go.
I have searched the internet for past few days and it is all js and objc both of which I have zero experience in. If someone could point me at a start or even guide me in what to do so I can learn.
In the cell I am displaying the users details like so:
let singleCell: CustomCell = tableView.dequeueReusableCellWithIdentifier("mySingleCellid") as! CustomCell
let userObject = appUsers[indexPath.row] as PFObject
singleCell.userName.text = userObject["name"] as? String
// etc
return singleCell

This part of the query
genreQuery.whereKey("genreSearch", equalTo:PFUser.currentUser()!)
is wrong because you're trying to check that an array of strings contains a user object (a pointer) - which will always fail.
While you have genreSearch on the server for the current user it's easier to just replace that part of the query with
genreQuery.whereKey("genreSearch", containedIn:PFUser.currentUser()!["genreSearch"])
which instead asks for the genreSearch array on each user tested to contain at least one of the current users array of genreSearch

I was going about it all wrong what I needed to do was take genreSearch from the Parse DB and store it in the app itself as a variable. This variable is then a key for my PFQuery and I use it to filter out the people that don't have it. I then use genreSearch as a condition skipping the people that don't have it and adding the people that do. I nearly have it cracked except for the last few coding of it. Instead of editing this question to ask for help I have asked and posted my new code to a new SO question

Related

How can I access firebase directory names as an array?

I'm trying to access department and course information for an iOS app for students to buy/sell textbooks.
Right now I have two pickerViews. I'd like to select the department in one and have the relevant courses load into the second. What kind of call can I make to get an array of just the department names when structured as below?
So here I would want to access an array ["AHSS", "AIE", "ANTH"]. Then afterwards, I'd make another call depending on the selection. For AIE, I'd want the array ["330", "340"].
I'm unsure how I can just get the directory names as an array and not the values they eventually lead to?
For anyone else that might have had this question. #Nikunj Kumbhani directed me in the right direction and I eventually got this which lists the key values.
myDatabase.child("departments").child("\(selectedDepartment ?? "AHSS")").observeSingleEvent(of: .value) { (snapshot) in
if let myData = snapshot.value as? NSDictionary {
for name in myData.keyEnumerator() {
stringList.append("\(name)")
}

Parse & Swift - How to Reduce Server Load/Requests?

I've been developing an application that has a home page, similar to that of Instagram's where the number of comments is displayed below a post as the user scrolls. Once the user taps on the label with the number of comments, the actuals comments will then load onto the screen. I've been trying to achieve something similar to that with my application, but I feel as if the method that I am doing this with is sending to many requests (queries) to the server in order to get the number of comments to display below each post. I was wondering if there was a more efficient/concise way to do this that would reduce the server load, but still have the same effect.
To add more context (Note: I'm using Parse):
I have a class named Posts which contains the posts
I have a class named Comments which contains all of the submitted comments. In order to obtain the comments for a particular post, I query for comments (which have a column named: "parentObjectID") whose column ("parentObjectID") matches with the parent post's object ID.
Example code is below:
let query = PFQuery(className: "Comments")
query.whereKey("parentObjectID", equalTo: objectIDs[indexPathNums])
query.order(byDescending: "createdAt")
query.findObjectsInBackground { (objects, error) in
if error != nil {
print("An error occured (USQVC Comments Query)")
}else {
if let tempArray = objects {
for comment in tempArray {
if let x = comment {
myArray.append(x)
}
}
commentsCount = myArray.count
myArray.removeAll()
}
}
}
Even though they've updated their UI, I just wanted to include a picture in case what I was describing was unclear:
I'd appreciate it if anybody could help me out with this. Thanks!
As you pointed out in your comment above, you certainly can use the function incrementKey to increment the numberOfComments.
The best part is, this method is atomic, that means no matter how many people like the same post at the same time, say 5 people like a photo at exact same time, numberOfComments will increment by 5 instead of 1.
So this function will be executed one by one instead of concurrently. You absolutely can use it.

Observing data on Firebase vs. checking an array?

I've got a situation where I've got a tableview being filled with names from Firebase.
When the view loads in, I pull all the necessary names from firebase, load them into an array, and base my tableview off that array.
I have an "add" button that takes whatever's in a text field and adds that name to both firebase and their list.
What I do not want to allow is for people to add a name that they have already added.
I'm pulling the names they've added from Firebase like:
users
209384092834
Names
Bob
Sue
so if the user were to type in Rob, it'd add it under that "names" bit, but if they typed in Bob/Sue it wouldn't allow them to add that again.
The two ways I see of doing this are to check if the name a user is wanting to add is in the array I'm filling on load, or to check against the names that are under their Names child on firebase.
Are there any strong arguments for using one over the other. Is it a "big deal" to run an observer to firebase? I feel like using firebase here is "safer" than checking the array..like what if the users net is so or inturrupts, the array hasn't filled up yet, and they type in a name to add, add it, and everything is just a mess. I don't even know if something like that COULD happen.
Any advice here on which direction to take and why?
Important :- Never use Arrays or Tuples to store in Firebase Database, always prefer Dictionary
Make your DB look something like this :-
{ users : {
209384092834 : {
Names: {
Bob : True,
Sue : True
}
}
}
}
I would suggest you use a third path :-
Check if that name exists by referring to that child node and checking by taking a particular snap of that path instead of the entire list..
rootRef.child("users").child(timeStamp).child("Names").child(textField.text!).observeEventType(.Value, withBlock: {(snapshotRecieved) in
if snapshotRecieved.exists()
{
//Show Alert that user Exists i.e if user is rob/sue in your case
}else{
let ref = rootRef.child("users").child(timeStamp).child("Names")
ref.observeEventType(.Value, withBlock: {(snapshot) in
if let namesDict = snapshot!.value as? NSMutableDictionary{
namesDict.setObject("True",forKey:textField.text!)
ref.setValue(namesDict)
}
})
}
})

Firebase fetch multiple results

I was wondering if there is any way with swift and firebase, to get multiple numbers from the same child? and average all the numbers.
Example,
Every authorized user saves a number to the database. Then when they look at there number in a saved section it averages all other users numbers and compares it against theres.
I can get all the data to save, and I can get the data to send back info, but it only sends one number from the list
I have tried different ways but I can't get it to work. Any help would be awesome.
I would post some code but none of it is working real well
Thanks again in advance
For your example, you would use childByAutoID to create a random unique identifier for your user. To get multiple values from one child you would use a comma, when setting the childs value. Like this,
struct getInput {
var inputType: String!
}
//copying and pasting this code into your code will not work, however
//you should be able to understand why and how this works.
let getUsername = getInput(inputType: usernameTextField.text!)
let getPassword = getInput(inputType: passwordTextField.text!)
let userReference = database.reference().child("users")
userReference.setValue(getUsername.inputType)
let childByAutoID = userReference.childByAutoId()
childByAutoID.setValue(["username": getUsername.inputType, "password": getPassword.inputType])

Connecting remote search results with local database using CoreData

Assume we have simple data model with single entity User; simple tableView_friends with fetchedResultsController_friends for show users - friends.
Assume we have search bar for searching all (not only friends) users in service, and for every typed in it character we perform search request to server, which return to us somehow filtered by character User objects. Some of this objects can already be inside local database. By app logic we don't really must save all this results in local database forever (but ok, we can, we can clear local database time to time); on other hand, if we will perform any action on some searched user, we must store this user. We want to show list of searched user in other tableView_search with fetchedResultsController_search.
Question: should I use same context for fetchedResultsController_friends and fetchedResultsController_search? If no, how can I handle situation, when I wish to edit searched user, which already exists in database and probably already local edited? If yes, how can I setup predicate for fetchedResultsController_search (server perform its own logic for search by character, which can be changed) for show exactly same result as from server?
We recently implemented a search feature in our application and had a similar issue, We had local data in core data and also remote data from our API.
You have a few options that we explored:
Save your data into core data from the API as it is retreived and
then the fetched results controller will do the rest
Manage the merge of the data yourself, you can still use NSFetchedResults controller to an extent but need to do more work
We didn't want to save all of the information returned from the API unless it was needed (the user selected it), so we come up with a simple solution that worked for our app. This may not work directly for your app, you may need a completely different solution or change some of the things we done to suit.
Firstly, To explain what we are dealing with, we had a Article entity in core data which contains around 25 properties, the API returns article objects as JSON data with the same data.
What we decided to do was to create a class which represents a simple version of an article (just enough data to show in a list view and reference it later in the API or core data) which looked something like this:
class SearchResult: NSObject {
var id:String?
var title:String?
var imageUrl:String?
var url:String?
// core data entity
init(article:Article) {
self.id = content.contentId
self.title = content.title
self.featuredImageURL = content.absoluteImagePath()
self.urlAlias = content.urlAlias
self.publishedAt = content.publishedAt
}
init(articleDictionary:NSDictionary) {
self.id = articleDictionary.objectForKeyNotNull("id") as? String
self.title = articleDictionary.objectForKeyNotNull("title") as? String
self.url = articleDictionary.objectForKeyNotNull("url") as? String
if let imageUrl = articleDictionary.objectForKeyNotNull("imageUrl") as? String {
self.imageUrl = imageUrl
}
}
}
Now using this, we can create once of these from either the core data results or from the API results. Our tableview datasource is just an array
var dataSet = [SearchResult]()
We use the NSFectchResultsController delegate methods to add/remove/re-order core data elements from the dataSet after the initial load and when we get API data we'll do something like:
dataSet = Array(Set(apiResponseArray + dataSet))
This will take an array of SearchResult items from the API, merge them with the current result set and remove duplicates. casting to a set and then back to an array will give you an array of unique results as a Set is made of unique values only.
See this reference which should help with how the delegate methods would work

Resources