New to Parse backend and coding all together.
Looking to create a "Favorite" function in my app so that users can save products that they like for later by tapping a simply UIButton.
I'm simply trying to figure out how to save the toUser (user whose product post is being favorited) and the fromUser (user who is doing the favoriting) in Parse.
Here is my code:
#IBAction func tagProductButton(sender: AnyObject) {
var favoritePost = PFObject(className: "Tag")
favoritePost["tagger"] = PFUser.currentUser()
favoritePost["productId"] = ??
favoritePost["userTagged"] = ??
favoritePost.saveInBackground()
}
"tagger" = toUser
"userTagged" = fromUser
Thank you in advance! remember, I've only been coding for about a month now
You should set the columns product and userTagged (from the Tag table) as Pointers if they are not already. Don't just use the ID as a String. You will need a reference to the Product (parse object) being tagged, so your code could look something like:
favoritePost["product"] = product
favoritePost["userTagged"] = product["user"]
You may not really need the userTagged field on the Tag table, since the user tagged is probably already attached to the product, but this may may other searches easier.
Related
Using realm I have setup 2 objects: User and Question.
Each User answers a Question, this is stored in a third object like so:
class Answer : Object {
dynamic var user: User?
dynamic var question: Question?
dynamic var answerText = ""
}
What I need to do, is given a user object and a question object, get the related Answer object. In semi-SQL pseudo-code something like:
SELECT FROM Answers WHERE user = User and question = Question
So...How do I achieve this?
Bonus points if there is a way of not having to fetch the question object and instead use the primary key of that object (i have the user object, but only a question id, so i have to resolve the question object first), so something like:
SELECT FROM Answers WHERE user = User and question.id = Question.id
Also because realm doesn't load the entire object into memory until you need it, I don't think it's capable of that.
Note: I've simplified by problem a little. There is a good reason I have the no primary answer id, so please don't tell me to add one.
You will need to change your User model to include an inverse relationship with Answer. This will automatically include all answers where user equals the current user.
Then you will need to query the users, access their answers property and filter that based on the questions.
I have created these skeleton classes so that I can test the query, I know that your User and Question class might be different, so change the primaryKey and question filter to suit your exact class definition.
class User: Object {
let answers = LinkingObjects(fromType: Answer.self, property: "user")
dynamic var userName = ""
override class func primaryKey()->String {
return "userName"
}
}
class Answer : Object {
dynamic var user: User?
dynamic var question: Question?
dynamic var answerText = ""
}
class Question: Object {
dynamic var title = ""
}
let questionId = ""
let questions = realm.object(ofType: User.self, forPrimaryKey: "userName")?.answers.filter("question.id = %#",questionId)
I've been learning iOS development for the past three weeks, I'm currently following a course on Udemy so far so good.
However I'm following one of the lectures whereby we build an Instagram Clone.
The instructor is using three arrays which are as follows:
var usernames = [""] // Stores all usernames
var userIds = [""] // Stores all Id's of the given usernames
var isFollowing = [false] // Stores where or not you're following that user
To me trying to keep track of what userId goes with what username using two arrays is basically an accident waiting to happen so I decided to set off and find a more feasible approach. I reverted back to my .Net days and decided to create a list so I went and created a class as follows:
class Users{
var Username : NSString = ""
var UserId : NSString = ""
var Following : Bool = false
}
Now inside my ViewController I make a call to Parse which returns me a list of users and I'm basically trying to loop through the response, and add them to the list class as shown here:
var t = [Users]() // After googling the web, this seems to be the syntax for a list declaration ?
let u = Users()
for object in users{
if let o = object as? PFUser {
u.Username = o.username!
u.UserId = o.objectId!
u.Following = o.IsFollowing!
self.t.append(u)
}
}
print(self.t)
Now when I print this to the console I see the following:
ParseStarterProject_Swift.Users
As I have one user at present, however when I try to loop through T and display the username in the console it doesn't display anything.
for x in t {
print(x.Username)
}
Your basic intuition is correct, it's better to have an array of custom objects, not multiple arrays.
Regarding making it more Swifty, consider your Users type. You might want something like:
struct User {
let username: String
let userId: String
let following: Bool
}
Note,
property names should start with lowercase letter;
Users should probably be called User, as it represents a single user;
we don't generally initialize values to default values like that, but rather specify them in the initializer;
we probably use String not NSString;
if a property cannot change, you'd use let, not var;
properties begin with lower case letters;
Then you can do something like:
var t = [User]()
for object in users {
if let o = object as? PFUser {
t.append(User(username: o.username!, userId: o.objectId!, following: o.IsFollowing!)
}
}
print(t)
Clearly, with all of those ! forced unwrapping operators, you'd want to be confident that those fields were populated for all of those properties.
Using struct is nice because (a) it's a value type; (b) you get the initializer for free; and (c) you can just print them. If you really wanted User to be a reference type (a class), you'd do something like:
class User {
let username: String
let userId: String
let following: Bool
init(username: String, userId: String, following: Bool) {
self.username = username
self.userId = userId
self.following = following
}
}
And if you wanted to be able to just print them, you'd define it to conform to CustomStringConvertible:
extension User: CustomStringConvertible {
var description: String { return "<User; username = \(username); userId = \(userId); following = \(following)>" }
}
With the class, you can feel free to change that description computed property to show it in whatever format you want, but it illustrates the idea.
You are correct in considering that keeping track of what userId goes with what username using two arrays is dangerous, you in the correct direction with your approach.
First, I would just like to suggest that you use correct naming convention:
Classes should be singular (except in very specific cases).
Variable/property names should begin with lowercase.
This would mean that your user class should look like this:
class User {
var username : NSString = ""
var userId : NSString = ""
var following : Bool = false
}
I will keep your existing naming use for the next part. The main problem with your code is that the variable "u" is a object which you create only once and then modify it. You should be creating a new "Users" object for each user instead of modifying the original. If you don't do this you will just have an array with the same user multiple times. This is how your code would look now:
var t = [Users]()
for object in users {
if let o = object as? PFUser {
let u = Users()
u.Username = o.username!
u.UserId = o.objectId!
u.Following = o.IsFollowing!
self.t.append(u)
}
}
print(self.t)
Next you mention that when you print to console you see the text: ParseStarterProject_Swift.Users, that is because Swift does not automatically print a pretty text with the content of your object. In order for it to print something more detailed, your "Users" object would need to implement the CustomStringConvertible. You can see a more detailed answer about that here: how-can-i-change-the-textual-representation-displayed-for-a-type-in-swif.
Lastly, you mention that when you loop trough "t" and display the username in the console it does not display anything. This is caused by one of two things:
Because there are no users being returned from parse, so the "t" array is actually empty. Try print(t.count) to see how many objects are in the array.
Because your "Users" object declares an empty string "" as the default username and the username is not being set correctly when getting the data from the parse. Which means that it IS actually printing something, just that it is an empty string. Try defining a different default value like var username : NSString = "Undefined" to see if it prints something.
Good luck learning swift!
I am creating a Sign Up Table on Swift using Parse to store data. Users can select maximum of 4 activities from the given list of activities.
I want the table to store all the 4 records of selected separately. But the code only makes one record!
Here is the code snippet-
//Creating a NSMutableSet to avoid multiple selection of same sport.
var tempSport = NSMutableSet()
//The vales in tempSport are : ["Activity1", "Activity2", "Activity3", "Activity4"]
#IBAction func getStartedButton(sender: AnyObject) {
if tempSport.count < 1
{
//Display Alert
}
else
{
var sendSport = Array(tempSport.allObjects)
print(sendSport)
for sport in sendSport
{
SportList["SportPlayer"] = user.username
SportList["SportPlaying"] = String(sport)
SportList.saveInBackground()
}
}
}
Image Link. Click here to see the expected output and output produced
Just save the object with an Array instead of 4 individual objects
let object = PFObject(className: "Your Class Name"
object["Sport Playing"] = [activity1, activity2, activity3, activity4]
object.saveInBackground()
Hope this helps.
Are you sure that you want to create a up to four sport objects for each user? That could end up becoming a lot of data in your database. I would recommend creating a a list of sports in the Parse database (soccer, football, tennis, etc) and create an array of users for each sport. For example, you would have Soccer under SportPlaying and an array of user objectIds under SportPlayer. Lets say you have a user with an objectId of H67c0uTUO1 that says they play Soccer, Basketball, and Lacrosse. You could send this data up to Parse by querying for the name of each sport that the user plays and adding the user's objectId to to the array using .addObject. Then, if you want a list of users who play that certain sport, you would query for using:
let sportQuery = PFQuery(className: "_User")
sportQuery.whereKey("objectId", containedIn: yourArrayFromSportPlayerForSpecificSport as! [anyObject])
let query = PFQuery.orQueryWithSubqueries([sportQuery])
query.findObjectsInBackgroundWithBlock { (results: [PFObject]?, error: NSError) -> Void in {
}
I have an application setup so that there is an array that contains objectId's that the current user is friends/connected with. I'm attempting to create a button that deletes a single objectId from that array (kind of like an unfriend button). I'm currently passing the objectId value from a query in one view controller to another view controller that actually contains the friends profile and the delete/unfriend button. I'm getting the actual objectId as a string in the friends profile and I can print the objectId but I can't seem to figure out a way to delete this single objectId from the array.
Using PFUser.currentUser()?.removeObject() gives me an invalid field name error.
Anyone have an insight for me?
Let me know if there is more clarification needed! Thanks in advance.
Update
I have tried to also utilize this as my object code. userObjectId is received from another view controller and grabs the proper objectId of what I'd like to remove from the current users "accepted" array. This code gives me an error of "NSInternalInconsistencyException reason: Tried to save an object with a pointer to a new, unsaved object" I've tried to remove this objectId from the array in several different ways but can't seem to get it to stick. Thanks again.
var userObjectId = ""
var object: PFObject = PFObject(className: "User")
object["objectId"] = userObjectID
PFUser.currentUser()?.removeObjectsInArray([object], forKey: "accepted")
PFUser.currentUser()?.saveInBackground()
let object = PFObject(withoutDataWithClassName: "Your Class", objectId: "objectId from array")
object.deleteInBackgroundWithBlock { (success, error) -> Void in
if error == nil && success == true {
//Delete the objectId from the array
}
}
This will also remove the object from any array that holds it.
I've a small problem. I am working on an App using Parse backend. So my problem is:
i've retrieved all the objectIds of users in _User class like this:
var userIds = [String]()
var userQuery = PFUser.query()
userQuery?.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if let users = objects {
self.userIds.removeAll(keepCapacity: true)
for object in users {
if let user = object as? PFUser {
if user.objectId != PFUser.currentUser()?.objectId {
self.userIds.append(user.objectId!)
}
}
}
}
println(userIds)
})
Now i want to save all the userIds which are stored in the array "userIds" to a column in a class named "Something". so the code will be like:
var something:PFObject = PFObject(className: "Something")
something["users"] = // have problem here
something.saveInBackground()
if i put userIds here, it gives an error.. because userIds is an array but column "users" is String.. but also i want to save all the userIds seperately in the column "users" like it is saved in objectId column of _User class..
I'm new here and I can't comment in the reply where you asked for help. Anyway, what do you want now is save only new users, as you said:
I just want to save the users which are not there
Well, there are some suggestions:
You can retrieve your User class and check for new users manually in the app and then send the new ones to your Class.
You can create a new column in your User class like "saved" where contains a boolean indicating if you already saved this user to your custom class or not. Remember also, of always do
user["saved"] = false
while creating a new user and then simply do:
let search = PFQuery(className: "_User")
search.whereKey("saved", equalTo: false)
to get these non saved users.
I strongly recommend the second option.
Ok, the case that you describe should look something like this:
var userIds = [String]()
var userQuery = PFUser.query()
userQuery?.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if let users = objects as? [PFUser] {
var somethingObjects = [PFObject]()
self.userIds.removeAll(keepCapacity: true)
for user in users {
if user.objectId != PFUser.currentUser()?.objectId {
self.userIds.append(user.objectId!) // I don't know if this is necessary anymore for your usecase
var something:PFObject = PFObject(className: "Something")
something["users"] = user.objectId
somethingObjects.append(something)
}
}
PFObject.saveAllInBackground(somethingObjects)
}
})
In this case, you create a number of something objects that are saved, but you might want to rename the "users" column to "user", since you only save one objectId per row.
I haven't worked with parse in a while, so there might be a way to optimize the number of API calls you have to make.