How to remove object from array in Parse with Swift 2.0 - arrays

I have an array of items in a column in Parse.
I am able to fetch that array with the code :
let query:PFQuery = PFQuery(className: "Names")
query.whereKey("date", greaterThan: NSDate())
query.findObjectsInBackgroundWithBlock {
(object, error) -> Void in
if object != nil
{
if(object!.count != 0)
{
for messageObject in object! {
self.arrayNames = ((messageObject as! PFObject)["arrayNames"] as? [String])!
}
} else {
print("No Objects")
}
}
} // self.arrayNames = ["Aruna", "Bala", "Chitra", "Divya"]
In this I want to delete an single item and again save it to the parse.
I can delete it locally as removeAtIndex but how can I remove that from Parse?

After you have retrieved your array, remove the one item you do not want, reassign the new array to the retrieved object and then save.

In the names, if I wanted to delete Aruna,
self.arrayNames.removeAtIndex(0)
Then to update this array in Parse,
let query:PFQuery = PFQuery(className: "Names")
query.whereKey("date", greaterThan: NSDate())
query.findObjectsInBackgroundWithBlock {
(object, error) -> Void in
if object != nil {
object!.setValue(self.arrayNames, forKey: "arrayNames")
object!.saveInBackgroundWithBlock {
(success: Bool, error: NSError?) -> Void in
if(success) {
print("Success")
} else {
print("Error")
}
}
}
}
I found that, i cant directly delete an single element from an array in a column in Parse. So I am updating that array with my local array. It works!

If you read the docs you will find one of the delete methods useful such as deleteInBackgroundWithBlock:
object.deleteInBackgroundWithBlock { (succeeded: Bool, error: NSError?) -> Void in
// do whatever you need to do
}

Related

CloudKit nil optional error

I am trying to load, then modify and resave an array.
Here is code, modify func is the top one:
func modifyUserGroupsRequestee(){
print("step2")
acceptedUsersArray.append(groupNameLbl.text!)
//error
userGroupRecordToUpdate.setObject(acceptedUsersArray as CKRecordValue?, forKey: "userGroups")
database.save(recordToUpdate) { (savedRecord, error) in
if error != nil{
print(error.debugDescription)
}else{
print("SAVED RECORD")
}
}
}
func resaveUserGroups(){
print(addedUser)
print("step1")
// add group to requestees user groups
let pred = NSPredicate(format: "username = %#", "\(addedUser)")
let query = CKQuery(recordType: "PersonalUser", predicate: pred)
let operation = CKQueryOperation(query: query)
//operation.resultsLimit = CKQueryOperationMaximumResults
operation.recordFetchedBlock = { (record: CKRecord!) in
if record != nil{
self.userGroupRecordToUpdate = record
// self.acceptedUsersArray = (record.object(forKey: "userGroups") as! Array)
print("usergroup names:: \(self.acceptedUsersArray)")
if let acceptedUserArrays = record.object(forKey: "userGroups") as? [String] {
// self.feedTableView.reloadData()
self.acceptedUsersArray = acceptedUserArrays
print("looks like we r going forward")
self.modifyUserGroupsRequestee()
// }
//self.feedTableView.reloadData()
print(groupNames.count)
print(self.acceptedUsersArray)
}
}
database.add(operation)
//self.tableView.reloadData()
// print(leaderboardInfo.count)
}
}
The function prints step1 but never gets to step2. In the bottom function, I have an if let statement I tried to create to solve my nil issue (I commented my previous code above that line- self.acceptedUsersArray... Anyway, I believe I am implementing the if let statement incorrectly, because no data is loaded, even though there is data in cloud kit.
And I do have my personal user cloudKit records set up, here's a pic:
You should try to keep your code always indented consistently.
(In Xcode editor, Cmd+A (Select All), then Ctrl+I (Re-Indent).)
With confusing comments removed, your resaveUserGroups shows as:
func resaveUserGroups() {
print(addedUser)
print("step1")
// add group to requestees user groups
let pred = NSPredicate(format: "username = %#", "\(addedUser)")
let query = CKQuery(recordType: "PersonalUser", predicate: pred)
let operation = CKQueryOperation(query: query)
operation.recordFetchedBlock = { (record: CKRecord!) in
if record != nil {
self.userGroupRecordToUpdate = record
print("usergroup names:: \(self.acceptedUsersArray)")
if let acceptedUserArrays = record.object(forKey: "userGroups") as? [String] {
self.acceptedUsersArray = acceptedUserArrays
print("looks like we r going forward")
self.modifyUserGroupsRequestee()
print(groupNames.count)
print(self.acceptedUsersArray)
}
}
database.add(operation)
}
}
Omitting some parts to clarify:
func resaveUserGroups() {
//...
operation.recordFetchedBlock = { (record: CKRecord!) in
if record != nil {
//...
}
database.add(operation)
}
}
The line database.add(operation) exists inside the recordFetchedBlock.
You may need to fix some more parts (that's another story), but at least, you need to move the line out of the closure to execute the operation you have created:
func resaveUserGroups() {
//...
operation.recordFetchedBlock = { (record: CKRecord!) in
if record != nil {
//...
}
//database.add(operation)
} //↓
database.add(operation)
}
I just solved it. Apparently there always needs to be some kind of value inside the array even if it was just created. I was trying to query an array that existed in the recordType, but not yet under the specific record.

Warning: A long-running operation is being executed on the main thread. try! get data(). Swift

I know that the error comes from try! converyPFFile.getData()
Wondering how to get rid of the error if I don't want to get data in background because if I get data in background, the image cannot be appended to the array. Thanks.
query.findObjectsInBackgroundWithBlock { (objects: [PFObject]?, error: NSError?) -> Void in
for object in objects! {
self.senderArray.append(object.objectForKey("sender") as! String)
self.messageArray.append(object.objectForKey("message") as? String)
if object.objectForKey("photo") != nil {
if let converyPFFile = object.objectForKey("photo") as? PFFile{
let temp = try! converyPFFile.getData()
let image = UIImage(data: temp)
self.photoArray.append(image)
}
} else {
self.photoArray.append(nil)
}
}
// create a chat interface
if self.senderArray[i] == self.userName {
if self.messageArray[i] != nil {
// using scroll view
// create username label
// create message text label
} else {
// using scroll view
// create username label
// create message image
messageImage.image = self.photoArray[i]
}
....
I found this warning is coming from Parse. You can overcome it by using getDataInBackgroundWithBlock and then append to your array within that block. Therefore your code becomes something like (untested and I think I have mis-paired braces):
query.findObjectsInBackgroundWithBlock { (objects: [PFObject]?, error: NSError?) -> Void in
for object in objects! {
self.senderArray.append(object.objectForKey("sender") as! String)
self.messageArray.append(object.objectForKey("message") as? String)
if object.objectForKey("photo") != nil {
if let converyPFFile = object.objectForKey("photo") as? PFFile{
converyPFFile.getDataInBackgroundWithBlock { (data: NSData!, error: NSError!) -> Void in
let image = UIImage(data: data)
self.photoArray.append(image)
}
} else {
self.photoArray.append(nil)
}
}
// create a chat interface
if self.senderArray[i] == self.userName {
if self.messageArray[i] != nil {
// create username label
// create message text label
} else {
// create username label
// create message image
messageImage.image = self.photoArray[i]
}
....
It is possible to execute your task in the background while still making sure that appending to your photo array is done on the main thread.
You simply need to dispatch that particular code to the main thread.
See example below:
query.findObjectsInBackgroundWithBlock { (objects: [PFObject]?, error: NSError?) -> Void in
for object in objects! {
self.senderArray.append(object.objectForKey("sender") as! String)
self.messageArray.append(object.objectForKey("message") as? String)
if object.objectForKey("photo") != nil {
if let converyPFFile = object.objectForKey("photo") as? PFFile{
let temp = try! converyPFFile.getData()
let image = UIImage(data: temp)
// this will cause self.photoArray.append to be executed on the main thread
dispatch_async(dispatch_get_main_queue(),{
self.photoArray.append(image)
})
}
} else {
// this will cause self.photoArray.append to be executed on the main thread
dispatch_async(dispatch_get_main_queue(),{
self.photoArray.append(nil)
})
}
}
// create a chat interface
if self.senderArray[i] == self.userName {
if self.messageArray[i] != nil {
// create username label
// create message text label
} else {
// create username label
// create message image
messageImage.image = self.photoArray[i]
}

Replacing Object In Parse

I'm trying to delete a saved Object in Parse and Replace it. My code is not working. My Parse Database is only adding objects with the same array name. Here is my code.......
#IBAction func finalSaveBTN(sender: AnyObject)
{
videoARY = [videoId, vidTitleText, vidDescription, vidIMG]
let videoSave = PFObject(className: "UserVideos")
videoSave.deleteInBackgroundWithBlock { (success, error) -> Void in
if success == true
{
videoSave["userObjectId"] = PFUser.currentUser()!.objectId
videoSave["vid\(self.saveValueLBL.text!)"] = self.videoARY
videoSave.saveInBackgroundWithBlock { (success, error ) -> Void in
if success == true
{
print("Succesfull")
}
}
}
else
{
print("Error")
}
}
}
I would like to delete the object, but if anyone knows how to successfully update a Parse array that'll help too. Thank you.
*** UPDATE ****
I'm trying this, not working either, what could be wrong....
#IBAction func finalSaveBTN(sender: AnyObject)
{
videoARY = [videoId, vidTitleText, vidDescription, vidIMG]
let videoSave = PFObject(className: "UserVideos")
let query = PFQuery(className: "UserVideos")
query.whereKey("userObjectId", equalTo: PFUser.currentUser()!.objectId!)
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
for object in objects!
{
let vid = object["vid\(self.saveValueLBL.text!)"]!
vid!.delete()
}
}
videoSave["userObjectId"] = PFUser.currentUser()!.objectId
videoSave["vid\(self.saveValueLBL.text!)"] = self.videoARY
videoSave.saveInBackgroundWithBlock { (success, error ) -> Void in
if success == true
{
print("Succesful")
}
}
}
**** UPDATE *****
I'm deleting all vids for videoSave. If you have any suggestions on how to target specific array I'll appreciate it. I'll try to rework and save as simply PFFile
#IBAction func finalSaveBTN(sender: AnyObject)
{
videoARY = [videoId, vidTitleText, vidDescription, vidIMG]
let videoSave = PFObject(className: "UserVideos")
let query = PFQuery(className: "UserVideos")
query.whereKey("userObjectId", equalTo: PFUser.currentUser()!.objectId!)
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
for object in objects!
{
if let vid = object["vid\(self.saveValueLBL.text!)"]!
{
vid.deleteInBackground()
vid.saveInBackground()
}
}
}
videoSave["userObjectId"] = PFUser.currentUser()!.objectId
videoSave["vid\(self.saveValueLBL.text!)"] = self.videoARY
videoSave.saveInBackgroundWithBlock { (success, error ) -> Void in
if success == true
{
print("Succesful")
}
}
There're a few obstacles here, let me walk you through them.
1:
let videoSave = PFObject(className: "UserVideos")
videoSave.deleteInBackgroundWithBlock {...}
The first problem is that you're trying to delete a brand new object you're creating, which hasn't been saved to the server yet. That's what let ... = PFObject(className:...) does, it creates a new object.
2: Updating parse arrays is really easy! If you have the object in your code, you can simply change your object with functions like, or , and then use .saveInBackgroundWithBlock, and Voila! If you don't have the object, you'll need to first create a version of your object without the data from the server, and then have it fetch it's info from your parse server, and THEN you can modify, and save it. Here's how you'd do those two things
PFObject(withoutDataWithObjectId: objectId)
PFObject().fetchIfNeededInBackgroundWithBlock({})
Finally, if you don't have the objectId of your object, then you'll have to find it using a PFQuery, and then using there .whereKey(...) to filter until found.
3: Are you aware that by calling videoSave["vid\(self.saveValueLBL.text!)"] = self.videoARY, you're saving to a column in your parse server named vid+the string of self.saveValueLBL.text!? You might be creating parse columns on the fly. Just a heads up!
4: Another problem is the videoSave["vid\(self.saveValueLBL.text!)"] = self.videoARY line. You can't save arrays of PFFiles. You'll have to keep that information in a separate column, with the designated type of PFFile.
5: In your new block of code, I don't think delete() works the way you think it does. If you wanna delete the entire object, use object.deleteInBackground(), if you just wanna delete an attribute of that object, you can use object.removeObjectForKey(...), and then object.saveInBackground().
Does this all make sense?
I didn't have to turn my array into a PFFile. I simply needed to add a second query. Basically I was querying all vids for current user. Addition of query.whereKeyExists narrowed it down to specific vid. It will also make my retrieving query easier. Here it is........
#IBAction func finalSaveBTN(sender: AnyObject)
{
videoARY = [videoId, vidTitleText, vidDescription, vidIMG]
let query = PFQuery(className: "UserVideos")
query.whereKey("userObjectId", equalTo: PFUser.currentUser()!.objectId!)
query.whereKeyExists("vid\(self.saveValueLBL.text!)")
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
for object in objects!
{
object.deleteInBackground()
object.saveInBackground()
}
}
let videoSave = PFObject(className: "UserVideos")
videoSave.setObject(self.videoARY, forKey: "vid\(self.saveValueLBL.text!)")
videoSave["userObjectId"] = PFUser.currentUser()!.objectId
videoSave.saveInBackgroundWithBlock { (success, error ) -> Void in
if success == true
{
print("Succesful")
}
}
}

I want to load array data from parse (swift)

I have to make a Comment, being an attempt to import the Array in the Parse. However, there is a problem.
When i try to load array from Parse, my output is ("Blah","Blah","Blah")
It's a tuple.... not a Array TT.....
How Can I bring in the Array from Parse Correctly?
it's my fetch function from parse
func fetchDataFromParse(){
var query = PFQuery(className:"Cafe")
query.whereKey("name", notEqualTo: "")
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
// The find succeeded.
// Do something with the found objects
if let objects = objects as? [PFObject] {
for object in objects {
self.imageText.append(object.objectForKey("name")! as! String)
self.commentArray = (object.objectForKey("comment")!) // This is array of comment from Parse!!
self.imageFiles.append(object.objectForKey("imageFile") as! PFFile)
self.messageTableView.reloadData()
}
}
} else {
// Log details of the failure
println("Error: \(error!) \(error!.userInfo!)")
}
}
}
You may need to use the following to cast this as a Swift Array:
self.commentArray = object.objectForKey("comment") as? [AnyObject]

Xcode , Parse - Storing values from an array object in a local array

I want the user to upload an image, and other users leave replies to that image, everything works fine so fat except i can't get save the replies inside the "replies" object/column in Parse into my local array in order to display them. it can't accept the : for in method.
override func viewDidLoad() {
super.viewDidLoad()
var query = PFQuery(className:"Posts")
query.getObjectInBackgroundWithId(cellID) {
(objects: PFObject!, error: NSError!) -> Void in
if error == nil {
println(objects["replies"])
self.repliesArray.append(objects["replies"] as String) // <- problem Here
} else {
println("Error retreiving")
self.displayError("Error", error: "Error retrieving")
}
}
}
If the replies column is a string, try this:
if error == nil {
var array = objects.objectForKey("replies") as [String]
for obj in array {
self.repliesArray.append(obj as String)
}
}

Resources