How to handle Empty PFFile objects (Parse backend, Swift) - ios

I have a list of users stored on Parse (backend), with a column called "profile_picture" that stores the user's profile picture. However, there are certain users without a profile picture, thus their profile_picture column is "undefined" (has no value).
I am using a searchbar to query through all the users and update the tableview with the user's profile pic and username. I do this by appending the username to var searchResults = String , and the profile pic to var searchImages = PFFile after the query like so:
let query = PFQuery.orQuery(withSubqueries: [usernameQuery!,fbUsername!, firstnameQuery!, lastnameQuery!]);
searchActive = true;
query.findObjectsInBackground { (objects, error) in
if error != nil {
print("There was an error getting userlist");
}
else {
if let users = objects {
self.searchResults.removeAll(keepingCapacity: false);
for object in users {
if let user = object.object(forKey: "username") as? String {
self.searchResults.append(user);
}
if let picture = object.object(forKey: "profile_picture") as? PFFile {
self.searchImages.append(picture);
}
self.searchTableView.reloadData();
}
The problem is that when the "profile_picture" column is empty, it does not append anything, which then means that the searchResults array (containing usernames) and the searchImages array (containing the PFFiles) have different sizes, which results in uncoordinated assignments of values. They are supposed to parallel arrays. And I'm using values in these arrays to assign values in a table cell.
Hope you guys understand my problem! Thanks in advance

So your username field is definitely not empty. I think you can add an else after you check whether profile_picture is nil or not. Like:
if let picture = object.object(forKey: "profile_picture") as? PFFile {
self.searchImages.append(picture);
} else {
self.searchImages.append(UIImage(named:"placeholder"))
}

Related

How to get a specific string value out of a json response from firebase

I have this data structure and I can't extract the right value:
users
private
userID
birthday: "birthdayValue"
username: "nathan"
firstName: "Nathan"
etc...
I'm making a search feature in my app to search for users via their username through the firebase realtime database:
let reference = Database.database().reference()
if(searchText != ""){
reference.child("users").child("private").queryOrdered(byChild: "username").queryStarting(atValue: searchText).queryEnding(atValue: searchText + "\u{f8ff}").observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.value is NSNull{
//handles errors
return
}
else{
if let user = snapshot.value as? NSDictionary {
for child in user{
print(child.key)
print(child.value)
}
}
else{
//null
}
}
})
at the moment the two print statements are printing these two results in the console every time I search:
wnszfmHilqNl6PG9khWtWkKUPtF3
{
birthday = 100;
dateCreated = "1579543450313.94";
description = nil;
email = "email#email.com";
firstName = Nathan;
instagramLink = nil;
lastLogin = "1579543450313.988";
lastName = Ellis;
profilePicURL = "url";
twitchLink = nil;
username = nathan;
youtubeLink = nil;
}
Which is expected, it prints the usersID (the key) and the value of the snapshot as a NSDictonary. I'm only interested in getting the username, nothing else. How would I extract the username out of this firebase snapshot so I can add their username as a string to an array for my search controller?
Obviously it needs to be dynamic as the userID will always be different.
Would I need to change my data model?
Your child.value seems to be a dictionary as well, so you can access it by:
if let valueDict = child.value as? [String: AnyObject] {
if let username = valueDict["username"] as? String {
// append username to results
print(username)
}
}
To print just the username, the smallest possible change is:
print(resultsLocalArray["username"])
This will fine, but will still retrieve the entire user node to the client, which uses more bandwidth than strictly needed.
If you find yourself frequently needing just the username of a user, or maybe even a list of username values across users, you might want to consider storing a node with just user names. So something like:
users
userID: "nathan"
But in your current setup you only retrieve the node for a single user, so I doubt the bandwidth savings are worth the additional complexity.

Loop in different type of array to find matched one

I'm trying to add a favorite button to my application using CoreData.
I have tableView and added favorite button inside of it to save the label when I press that specific row. I saved it successfully. But I want to populate the CoreData just once for that specific row.
var checkFav = [Fav]()
I created an array with Type Fav which is name of my class for CoreData to populate items I have to check they appear just once.
let result = try context.fetch(Fav.fetchRequest())
checkFav = result as! [Fav]
if checkFav.isEmpty{
let fav = Fav(context: context)
fav.name = name
appDelegate.saveContext()
}
Above you see i populated the array.
do{
let result = try context.fetch(Fav.fetchRequest())
checkFav = result as! [Fav]
if checkFav.isEmpty{
let fav = Fav(context: context)
fav.name = name
appDelegate.saveContext()
}
else{
let checkName = array[rowSelected]
for value in checkFav{
if value.name == checkName{
print("You already have this name ")
}
else {
let fav = Fav(context: context)
fav.name = name
appDelegate.saveContext()
}
}
}
} catch{
print("Error")
}
Let's says I have two name "john","martha" in CoreData if I press to button of "martha" it shouldn't add again. But because of my loop when it sees "john" in the array it thinks this name isn't matching so it's saving "martha" (same name) to CoreData.
How can I check my checkFav array which contains the upcoming name if it contains don't save it again. But if not, add to CoreData. I'm really confused.
Your way to check for an empty entity cannot work if you want to save multiple records.
Add a predicate to the fetch request and a result limit of 1 to check for the specific name.
This method takes the name as parameter and adds a record if the name does not exist. It returns true if an entry is added otherwise false. Further it passes a potential error.
func addFavorite(for name : String) throws -> Bool {
let request : NSFetchRequest<Fav> = Fav.fetchRequest()
request.predicate = NSPredicate(format: "name = %#", name)
request.resultsLimit = 1
if let _ = try context.fetch(request).first {
return false // record exists
} else {
let fav = Fav(context: context)
fav.name = name
appDelegate.saveContext()
return true // record added
}
}

Couchbase lite, Search query taking very long time

When I try to search the couchbase documents of size around 10K, the searching is taking very long time. Below are the code snippet. Can anyone optimize it or suggest me any alternative approach. Thank you.
1) Search function
func search(keyword:String) -> [[String:AnyObject]] {
var results:[[String:AnyObject]]=[]
let searchView = database.viewNamed(AppConstants().SEARCH)
if searchView.mapBlock == nil {
startIndexing()
}
let query = searchView.createQuery()
var docIds = Set<String>()
let result = try query.run()
while let row = result.nextRow() {
let key = "\(row.key)"
let keyArr = keyword.characters.split(" ")
for (index, element) in keyArr.enumerate() {
let keyItem = String(element)
if key.lowercaseString.containsString(keyItem.lowercaseString) {
let value = row.value as! [String:AnyObject]
let id = value["_id"] as? String
if id != nil && !docIds.contains(id!) {
results.append(value)
docIds.insert(id!)
}
}
}
}
}
2) Indexing
func startIndexing() {
let searchView = database.viewNamed(AppConstants().SEARCH)
if searchView.mapBlock == nil {
searchView.setMapBlock({ (doc, emit) in
let docType = doc[AppConstants().DOC_TYPE] as! String
if AppConstants().DOC_TYPE_CONTACT.isEqual(docType) {
self.parseJsonToKeyValues(doc)
for value in self.fields.values {
emit(value, doc)
}
self.fields.removeAll()
}
}, version: "1")
}
}
self.parseJsonToKeyValues(doc) will return me the key value store of my documents to index.
You're emitting the entire document along with every field for your view. This could easily cause your queries to be slow. It also seems unlikely you want to do this, unless you really need to be able to query against every field in your document.
It's considered best practice to set your map function right after opening the database. Waiting until right before you query may or may not slow you down.
See https://developer.couchbase.com/documentation/mobile/current/guides/couchbase-lite/native-api/view/index.html for more, especially the section labeled "Development Considerations".

Adding Relational Data to a Class From User Class - Parse & Swift

I have to classes in parse, one is showing the users and the other is showing tweets. Both of the classes has a username column. However, I need to add a relational data to the tweets class that will include the info of the user class object for each user. I tried lots of codes but I could not make it. The views of my two classes are attached. How would be the swift code that when someone enters a new tweet a relational input will also be created in the tweet class. My current tweet code is as below:
var tweetObj = PFObject(className: "tweets")
tweetObj["userName"] = PFUser.currentUser()!.username
tweetObj["profileName"] = PFUser.currentUser()!.valueForKey("profileName") as! String
tweetObj["photo"] = PFUser.currentUser()!.valueForKey("photo") as! PFFile
tweetObj["tweet"] = theTweet
tweetObj["tweetType"] = 1
if hasImage == true {
tweetObj["hasImage"] = "yes"
let imageData = UIImagePNGRepresentation(self.tweetImg.image)
let imageFile = PFFile(name: "tweetPhoto.png", data: imageData)
tweetObj["tweetImage"] = imageFile
} else {
tweetObj["hasImage"] = "no"
}
tweetObj.save()
println("tweet!")
self.dismissViewControllerAnimated(true, completion: nil)
}
http://i.imgur.com/blszoqn.png
http://i.imgur.com/fK4ghlq.png

NSPredicate & Parse Have it return results matching current user, but also username strings, from an array

Basically, I am working on a chat app. In order to retrieve "room" objects from parse, I am using a query like this:
func loadData(){
rooms = [PFObject]()
users = [PFUser]()
self.tableView.reloadData()
let pred = NSPredicate(format: "user1 = %# OR user2 = %#", PFUser.currentUser()!, PFUser.currentUser()!)
let roomQuery = PFQuery(className: "Room", predicate: pred)
roomQuery.orderByDescending("lastUpdate")
roomQuery.includeKey("user1")
roomQuery.includeKey("user2")
roomQuery.findObjectsInBackgroundWithBlock { (results, error) -> Void in
if error == nil {
self.rooms = results as! [PFObject]
for room in self.rooms {
let user1 = room.objectForKey("user1") as! PFUser
let user2 = room["user2"] as! PFUser
if user1.objectId != PFUser.currentUser()!.objectId {
self.users.append(user1)
}
if user2.objectId != PFUser.currentUser()!.objectId {
self.users.append(user2)
}
}
self.tableView.reloadData()
}
}
}
Now, this is working fine when it comes to find any rooms that the current user is a member of. What I want to do is that somewhere in the app, I also have an array of usernames(String type).
So the question is: how do I use the above NSPredicate, in such a way that the results/PFObject returned are also assured to match any of the usernames in the array for the usernames of user1 or user2.
(Basically, the idea is that, that array represents your contacts and if someone is no longer one you should no longer see that room)
Please pretty please? :<
You should create an array of the allowed users, including the current user and placeholder objects for all your other user ids (see objectWithoutDataWithClassName:objectId:). then use that array in your query with IN instead of testing for direct equality (=).

Resources