ios swift parse: get data out of parse and use them - ios

I find only in the docs how the query can look like to select data.
As far as I see, there is only one way to collect 1 or many results:
var query = PFQuery(className:"GameScore")
query.whereKey("playerName", equalTo:"Sean Plott")
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]!, error: NSError!) -> Void in
if error == nil {
// The find succeeded.
NSLog("Successfully retrieved \(objects.count) scores.")
// Do something with the found objects
for object in objects {
NSLog("%#", object.objectId)
}
} else {
// Log details of the failure
NSLog("Error: %# %#", error, error.userInfo!)
}
}
What I cant figure out (as I am a beginner!) is how to access the object data. Lets say I have the fields "name", how can I get it? What is the right syntax? Especially if I have more than 1 result?
For just one result I would do:
var name = object["name"] as String
myArray.append(name)
Even that can't be right? To use "var xx = xx" within the loop?
And what do I do when I have more than one result?
Other thought:
Declaring the var name: String! before I do the query and then:
name = object["name"] as String
self.myArray.append(name)
Returns the error: Immutable vaue of type [String] only has mutating members named 'append'
What would be the correct way to "work" with the data the query returns?
Another question: as those querys are async, they finished later and the method is "done" much more earlier, this way my array with names is empty when the view is shown and I receive the data at a later stage. What is the best practice here to have all data available before the view is delivered to the device?
Thanks so much!!

You can use objectForKey on your object. So instead of using var name = object["name"] as String you can use:
for object in objects {
var name = object.valueForKey("name") as String
}
But the other parts are absolutely fine. You can create an array somewhere in you code and then add the objects to it. to do that, you can loop through your objects and than add the objects to your array. Like that:
if error == nil {
// The find succeeded.
NSLog("Successfully retrieved \(objects.count) scores.")
// Do something with the found objects
for object in objects {
var name = object["name"] as String
myArray.append(name)
}
}
Because you can reuse the var name because every loop-element will be filled into the variable name and will erase the last value. But the array will get the new value appended. For example:
First loop. The value at the first index of your objects gets loaded into the object. For example with the value "John".
variable name's value is now the value of the object["name"] of the current loop. So name has the value John
Now you add the value to your array.
The second loop starts and the second element gets loaded inside object which now has the string Michael.
The Variable name's new value is now the value of object. So name's value is now Michael
and so on.

Related

iOS ParseError : Attempted to change an objectId to one that's already known to the Offline Store

I'm having a regular issue with my project, using Parse Server. First, I call a Parse Cloud function to populate a user's data list :
var dataSet: Set<MyData>?
func loadData(withParameters parameters: [String : Any]) {
PFCloud.callFunction(inBackground: "loadData", withParameters: parameters) { (success, error) in
if let objects = success as? [[String : Any]] {
let dataTable: [MyData] = objects.map({ (object) -> MyData in
let myData = MyData(dataSource: PFObject(className: "MyData",
dictionary: object))
myData.dataSource?.objectId = object["objectId"] as? String
return myData
})
if self.dataSet == nil {
self.dataSet = []
}
self.dataSet = Set(dataTable)
}
}
}
On the code mentioned above, I have to set the objectId because without this, no matter how many objects I fetch from Parse, when I reduce the array to a set with the last instruction I end up with only one object in it.
However, although this works, when I call this function again to update user's data, I get this error on the myData.dataSource?.objectId = temp["objectId"] as? String line :
Attempted to change an objectId to one that's already known to the Offline Store.
Tried to find some information about it but nothing very relevant...
Thanks for your help.
This is no really an answer, but too long for a comment:
I understand the following:
Your cloud function loadData returns success, which is an array of dictionaries or, probably in case of an error, nil. If nil is returned, you do nothing.
If an array is returned, it is referred as objects.
This array is then mapped to an array of elements of type MyData.
For every element returned from the cloud function, called object, a MyData instance is created using as dataSource a new PFObject instance of class MyData and properties given by the object dictionary.
I do not understand the next statement by which you assign the objectId of the object returned from the cloud function to the newly created PFObject: As far as I know, objectIds must be unique, and cannot (or at least should not) be assigned to other PFObjects.
But you said you have to do this or otherwise you end up with a single element in the resulting set (see below).
Anyway, you have now an array of MyData instances in dataTable.
Then, you initialise dataSet, which is not required since it will be overwritten anyway by your last statement.
This statement makes a set of the dataTable array.
Now, when you get a single element in this set in case you do not set the objectId property of the elements of the dataTable array, this means that all elements of this array are the same object. And this means that your init function
MyData(dataSource: PFObject(className: "MyData",
dictionary: object))
returns always the same object, and this object is made to a different one by setting its objectId property. This is very strange to me. Please check what it going on there.
Now if you call loadData once more to update the data, the cloud function probably returns some or all of the previous objects again. In this case, you would assign an old objectId, that has already been assigned earlier to a PFObject created in the map function, to another newly created PFObject created there, which is not allowed, thus the error message.
To cut it short:
If the same object is returned from the cloud function repeatedly, you must not create different PFObjects for them, and assign them the same objectId.
I hope this helps!
http://parseplatform.org/Parse-SDK-iOS-OSX/api/Protocols/PFSubclassing.html
If a subclass of PFObject conforms to PFSubclassing and calls PFObject.+registerSubclass, Parse framework will be able to use that class as the native class for a Parse cloud object.
For example:
1.Create your subclass of PFObject
import Parse
class Question: PFObject, PFSubclassing {
#NSManaged var category: String?
#NSManaged var text: String
#NSManaged var image: PFFile?
#NSManaged var answerType: String
#NSManaged var order: NSNumber
static func parseClassName() -> String {
return "Question"
}
}
class User: PFUser {
#NSManaged var fullname: String
}
2.Register subclass
extension Parse {
class func registerSubclasses() {
User.registerSubclass()
Question.registerSubclass()
}
}
3.Configure parse service once app launches
Parse.registerSubclasses()
Parse.enableLocalDatastore()
Parse.setApplicationId(PARSE_APP_ID, clientKey: PARSE_APP_KEY)
4.Now PFUser == User, Question == PFObject(className: "Question")
5.It works for Cloud Function
PFCloud.callFunctionInBackground("fetchQuestions", withParameters: parameters) { questions, error in
if let questions = questions as? [Question] {
completion?(questions, nil)
} else {
completion?(nil, error)
}
}
Note: the swift code may be old, there should be some changes if using higher swift version.

Access Parse Array Elements

I have an array column in Parse that is called homeAddress. Inside it is the following value for a single row:
["111111","222222"]
I wish to access this in swift. I created a local array called homeAddress of String type. How can I store the first element in the Parse column (i.e. 111111) in the first element holder in local array i.e. homeAddress[0]? And then I need to store the 222222 in the homeAddress[1].
So what you want is load this data from parse. You can just call a Parse Query get the results and work with them:
let query = PFQuery(className:"ClassName")
query.findObjectsInBackgroundWithBlock {
(results, error) -> Void in
if (error == nil) {
for object in results!{
let homeAddress = object["homeAddress"] as! NSArray
}
}else {
print(error?.userInfo)
}
}
One way would be to trim first and last character and just separate values by comma:
var test = "[\("111111"),\("222222")]"
var trimmedStringRange = Range<String.Index>(start: test.startIndex.successor(), end: test.endIndex.predecessor())
var homeAddress = test.substringWithRange(trimmedStringRange).componentsSeparatedByString(",")
If your local array is empty, you can add your values from the parse array like this (after getting the array from Parse):
localHomeAddress.append(homeAddress)
This will put all values from homeAddress array in localHomeAddress array.
If you use a splice, you can ensure you are adding the values from index 0:
localHomeAddress.insertContentsOf(homeAddress, at:0)

Delete an objectID from an array in Parse via Button

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.

Parse backend: key won't increment?

I'm trying to increment a number in my parse table under the column "votes". Here's my code:
func upVote() {
var reviewQuery: PFQuery = PFQuery(className: "reviews")
reviewQuery.whereKey("content", equalTo: reviewTextView.text)
reviewQuery.findObjectsInBackgroundWithBlock{
(objects:[AnyObject]!, error:NSError!)->Void in
if error == nil{
for object in objects{
println(object)
let review:PFObject = object as! PFObject
review.incrementKey("votes", byAmount: 1)
}
}
}
}
When I print the object in the console I can see that it is the correct object that I'm looking for. It looks like this:
<reviews:ZqgSVL1Tsd:(null)> {
content = "njk\n";
reviewer = "<PFUser:6387CJtYI1>";
votes = 1;}
But when I look at my parse end, the number of votes has not changed. What am I doing wrong?
Save the object with
review.saveInBackground()
after incrementing the key.
After you modify an object, however small the modification, you must save it after. You are not saving your changes to the object review.
You have several options for saving, including save(), saveInBackground(), saveEventually(), and more. See the documentation for PFObject for more information:
https://www.parse.com/docs/ios/api/Classes/PFObject.html#//api/name/save
For example, you could save the object synchronously with
review.save() and you could save the object asynchronously with review.saveInBackground().

PFQuery where array of pointers contains a certain PFObject

Objects in my class Deal have an attribute relatedContacts which is an array of pointers to Contact objects. I'm running the following query to determine whether the current Contact object is the target of a pointer in any Deal, prior to deleting the Contact.
let relatedContactObjects:NSArray = [self.contactObject] as NSArray
let relatedContactQuery:PFQuery = PFQuery(className: "Deal")
relatedContactQuery.fromLocalDatastore()
relatedContactQuery.fromPinWithName("Deals")
relatedContactQuery.whereKey("user", equalTo: PFUser.currentUser()!)
relatedContactQuery.whereKey("relatedContacts", containsAllObjectsInArray: relatedContactObjects as [AnyObject])
However this returns Parse Error 102: "Value type not supported for $all queries."
The Parse documentation says that containsAllObjectsInArray takes an NSArray, but Xcode shows a warning that NSArray is not implicity convertible to [AnyObject].
Any ideas how I can make this query work?
Edit: I looked at the contents of relatedContacts and it seems that each instance contains an array of dictionaries, example: [{"__type":"Pointer","className":"Contact","objectId":"BoLym053hX"},{"__type":"Pointer","className":"Contact","objectId":"AgpnxAFUBn"},{"__type":"Pointer","className":"Contact","objectId":"ob20tThdfp"}]
As suggested, I've also looked at the containedIn query constraint, but that is used to identify objects that are contained in a given array. I am trying to identify arrays that contain a given object.
Parse.com overloads equalTo: by allowing it to mean either: (a) a singular property equals the operand, or (b) an array property contains the operand. So you're objective is easily stated as follows:
relatedContactQuery.fromPinWithName("Deals")
relatedContactQuery.whereKey("user", equalTo: PFUser.currentUser()!)
relatedContactQuery.whereKey("relatedContacts", equalTo:self.contactObject)
Prior to the accepted answer, I also tried using loops to go through the arrays and identify whether they contained the current object, then incremented a count.
var dealsPointingToContactCount:Int = 0
func countDealsRelatedToContact() {
let dealsWithRelatedContactQuery:PFQuery = PFQuery(className: "Deal")
dealsWithRelatedContactQuery.fromLocalDatastore()
dealsWithRelatedContactQuery.fromPinWithName("Deals")
dealsWithRelatedContactQuery.whereKey("user", equalTo:PFUser.currentUser()!)
dealsWithRelatedContactQuery.whereKeyExists("relatedContacts")
dealsWithRelatedContactQuery.findObjectsInBackgroundWithBlock{(objects, error) -> Void in
if (error == nil) {
var dealsWithPointersToContacts:NSArray = objects! as NSArray
for deal in dealsWithPointersToContacts {
var dealContactsArray:NSArray = deal["relatedContacts"] as! [PFObject]
for contact in dealContactsArray {
if contact as! PFObject == self.object {
self.dealsPointingToContactCount++
println("Deals pointing to current contact: \(self.dealsPointingToContactCount)")
}
}
}
}
}
}

Resources