iOS - Update An Existing Object Not Working - (Copied syntax from iOS Object API) (Swift)

Hey,
I’m having a little trouble with updating an existing object in Swift.
I am able to Create & Delete Objects on the server perfectly, but for some reason the update is not working?
My Object - “userAdditionalDetails” (Holds the corresponding User’s ID as the ‘ownerId’ Value)

class userAdditionalDetails: NSObject {
 var isFirstLogin: String?
 var ownerId: String?
 var expired: String?
 var emailBackup: String? 
 
}

My Update Code - (In this case I am trying to update the value ‘isFirstLogin’ in the table “userAdditionalDetails” to the String: “False” (There is a reason I’m not using a Bool for this but thats not important right now)

func updateAdditionalDetailsSync(details: userAdditionalDetails) {
 
 let dataStore = Backendless.sharedInstance().data.of(userAdditionalDetails.ofClass())
 var error: Fault?
 details.isFirstLogin = "False"
 
 let updatedUser = dataStore.save(details, fault: &error) as? userAdditionalDetails
 if error == nil {
 print("Contact has been updated: \(updatedUser?.isFirstLogin)")
 }
 else {
 print("Server reported an error (2): \(error)")
 }
 }

The Update Method Call

self.updateAdditionalDetailsSync(userAdditionalDetails())

This is exactly how the Update Syntax appears in the API, so I’m not sure what I’ve done wrong or if this is an issue on your side…
Let me know!
I look forward to getting the update working, as you know, not understanding why a section of code isn’t working is a huge frustration haha…
Thanks,
Dieter Kunze

Hi Dieter,

Looking at your code, you’re getting “Contact has been updated: True” on the screen, right?

I’m having the same problem. Suddenly my App can’t update an existing object. I’m sure this was working some days ago. In my case I’m, using swift also and when executing datastore.save function it generates a message like: “Cannot persist object. Property ‘objectid’ must be set by the server. Make sure the property is not initialized when saving new object”.

But it’s not a new object. It’s an existing one…

Can I just say, I really appreciate & I am really impressed with the speed in which you guys handle user support here, thanks very much.

So I ran my project again now to check the console output like you asked, & an insert method i’ve successfully run about 30 times returned?

Server reported an error: Optional(FAULT = 'Server.Processing' [java.lang.RuntimeException: com.backendless.util.BackendlessTimeoutException: Could not acquire lock] <java.lang.RuntimeException: com.backendless.util.BackendlessTimeoutException: Could not acquire lock> )

Are you guys doing server maintenance or something?

That could explain my code not working & Daniels comment?

Thanks

At this moment is working again…

Are you constantly receiving this error message or just one time? Generally it means that server is under the load, but we’re already working on improving the performance in order to avoid this error.

Also, is the original error with update method gone now?

Hey Sergey,

With regard to the original code for this post, the console prints out “False” like you suggested it should, but the record on the server is now copied? ie. The record that I wanted to update still exists with the old values, & this code has made a new record above it with the new value, so I’m left with two records, old & new?

How do I alter this code to simply update the one existing record?

Hi Dieter,

You wrote:
"The Update Method Call

self.updateAdditionalDetailsSync(userAdditionalDetails())

"
But this code always creates new object userAdditionalDetails() with property isFirstLogin = “False”

If you need to update an existing record, you should:

  1. fetch it (or keep the result of previous save/update call),
  2. change some its properties,
  3. update it on the server.

I created these methods operating with your class:

    func saveNewUserDetails() -> userAdditionalDetails? {
        
        let detail = userAdditionalDetails()
        detail.isFirstLogin = "True"
        
        var error: Fault?
        let result = Backendless.sharedInstance().data.save(detail, error: &error) as! userAdditionalDetails
        if error == nil {
            
            print("User detail is saved: \(result.isFirstLogin)")
            return result
            
        } else {
            print("saveNewUserDetails: Server reported an error: \(error)")
            return nil
        }
    }
    
    func updateAdditionalDetailsSync(details: userAdditionalDetails) {
        
        let dataStore = Backendless.sharedInstance().data.of(userAdditionalDetails.ofClass())
        var error: Fault?
        
        details.isFirstLogin = "False"
        
        let updatedUser = dataStore.save(details, fault: &error) as? userAdditionalDetails
        if error == nil {
            print("Contact has been updated: \(updatedUser?.isFirstLogin)")
        }
        else {
            print("updateAdditionalDetailsSync: Server reported an error (2): \(error)")
        }
    }

then called them:

        let detail = saveNewUserDetails()
        if detail != nil {
            updateAdditionalDetailsSync(detail!)            
        }

Here is a log:
http://support.backendless.com/public/attachments/9a69013f2f0e51caeb2de1965d8d7e1e.png</img>
and result on the my app dashboard:
http://support.backendless.com/public/attachments/acc64ee0b4efc235a1ae0ea9c6bbafa1.png</img>
So, update works fine. Please try this approach and let me know how it goes for you.

Regards,
Slava

So, I see what you’re doing with your code, & I understand that to update the existing record you need to get the existing record (In your case you made it on the spot & passed it’s value (detail) to the UpdateSync method.

But in my case, the record for userAdditionalDetails is created when the user signs up on the app, so on the first run of the app the (detail) perimeter can be passed as you have done in your solution, but on the second run of the app the record still exists, it will not be deleted & remade, so the (detail) perimeter no longer exists?

How then can I update a record that was made in the past? I’m thinking maybe by running a where query & finding the record, then using its (detail) perimeter?

If so how can that be done? (When I run a where clause I get the return of type BackendlessCollection as you would expect)

Or what other solution do you suggest?

Thanks

Yes, you could use PersistenceService (Data) retrieving methods with ‘where clause’, for example:

let whereClause = "emailBackup = 'jack.daniels@foo.com'"

Or for current user it may be:

let whereClause = "emailBackup = '\(backendless.userService.currentUser.email)"

Edit:

Thanks for your help, my idea worked, I managed to update the old existing object by finding it using a where query, the code I’m using:

//Get the record 

    func getAdditionalDetailsRecord() -> userAdditionalDetails? {

        

        var detailTemp = userAdditionalDetails()

        

        //Error Handling

        Types.tryblock({ () -> Void in

        

        let whereClause = "ownerId = '\(self.backendless.userService.currentUser.objectId)'"

        let query = BackendlessDataQuery()

        query.whereClause = whereClause

        let detail = self.backendless.persistenceService.of(userAdditionalDetails.ofClass()).find(query)

        

        let currentPage = detail.getCurrentPage()

        

        print("Loaded \(currentPage.count) User objects:")

        

        for detail in currentPage as! [userAdditionalDetails] {

            

            print("Is First Login: ", detail.isFirstLogin)

            

            

            detailTemp = detail

            

        }

            

            },

                       catchblock: { (exception) -> Void in

                        

                        print("Exception: ", exception)

                        

                        self.error()

                        

        })

        

        return detailTemp

        

    }

    

    //Updates the record of type "details"

    func updateAdditionalDetailsSync(details: userAdditionalDetails) {

        

        //Error Handling

        Types.tryblock({ () -> Void in

            

        //-----

            let dataStore = Backendless.sharedInstance().data.of(userAdditionalDetails.ofClass())

            var error: Fault?

            details.isFirstLogin = "False"

            let updatedUser = dataStore.save(details, fault: &error) as? userAdditionalDetails

            if error == nil {

                print("Contact has been updated: \(updatedUser?.isFirstLogin)")

            }

            else {

                print("updateAdditionalDetailsSync: Server reported an error (2): \(error)")

            }

        //-----

            

            },

                       catchblock: { (exception) -> Void in

                        

                        print("Exception: ", exception)

                        

                        self.error()

                        

        })

            

    } 



To Call the Update:

//Update userAdditionalDetails - Set isFirstLaunch to "False"

                let oldObject = self.getAdditionalDetailsRecord()

                if(oldObject != nil) {

                    self.updateAdditionalDetailsSync(oldObject!)

                }

Thanks again!