Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

handling error in .saveAll() #188

Closed
lsmilek1 opened this issue Jul 8, 2021 · 4 comments
Closed

handling error in .saveAll() #188

lsmilek1 opened this issue Jul 8, 2021 · 4 comments
Labels
type:feature New feature or improvement of existing feature

Comments

@lsmilek1
Copy link
Contributor

lsmilek1 commented Jul 8, 2021

I am learning how to efficiently save objects in batch and how to properly handle the errors and I noticed that the code is throwing only one error it gets from the batch. Here is a simple example and I try to upload 2 objects that are not yet on the server - one with objectId = nil and other with defined objectId:

    objects.saveAll { result in
            switch result {
            case .success(let saveResults):
                for result in saveResults {
                    switch result {
                    case .success(let savedObject):
                        savedObjects.append(savedObject)
                    case .failure(let error):
                        assertionFailure("error saving oblect: \(error.localizedDescription)")
                    }
                }
            case .failure(let error):
                self.handle(error: error, syncCategory: .inboxObjects, localId: "all")
            }
        } 

What is a bit confusing for me is that the .success(let saveResults) is also a Result but the failure in that result seems to not be reached as the .saveAll() catch the failure according to lines:

callbackQueue.async {
if let parseError = error as? ParseError {
completion(.failure(parseError))
} else {
completion(.failure(.init(code: .unknownError, message: error.localizedDescription)))
}
}

So in this example, because I use allowCustomObjecId the first object throws:

ParseError code: missingObjectId

But the second object does not throw anything as the function does not come to the second object. What makes sense, as it tries to construct command and gets stopped by following guard (although I am getting lost in the code so I might be wrong):

if ParseSwift.configuration.allowCustomObjectId && objectable.objectId == nil {
throw ParseError(code: .missingObjectId, message: "objectId must not be nil")
}

My question would be then... If the .saveAll() fails due to one object in array, how can I find out what object it was so that I could exclude it from repeated upload or show warning in the UI?

@cbaker6
Copy link
Contributor

cbaker6 commented Jul 9, 2021

It seems like you are hitting this particular situation because of what I posted here.

I recommend choosing one of the options I mentioned. The try is throwing correctly:

let commands = try map { try $0.saveCommand() }

If you want to submit a PR and attempt to identify the specific object that throws, I will review it, but attempting to save mixed environment custom objectId's isn't recommended and not supported by the SDK.

@lsmilek1
Copy link
Contributor Author

lsmilek1 commented Jul 9, 2021

Do you mean what you posted here, right?

I will switch to server generated Ids as I have basically only one class that got custom objectId to save few cloud code queries. Therefore I believe this situation won't happen anymore as I see that createCommand() and updateCommand() do not throw and saveCommand() has only the .missingObjectId()

// MARK: Saving ParseObjects
static func saveCommand<T>(_ object: T) throws -> API.Command<T, T> where T: ParseObject {
if ParseSwift.configuration.allowCustomObjectId && object.objectId == nil {
throw ParseError(code: .missingObjectId, message: "objectId must not be nil")
}
if object.isSaved {
return updateCommand(object)
}
return createCommand(object)
}
// MARK: Saving ParseObjects - private
private static func createCommand<T>(_ object: T) -> API.Command<T, T> where T: ParseObject {
let mapper = { (data) -> T in
try ParseCoding.jsonDecoder().decode(SaveResponse.self, from: data).apply(to: object)
}
return API.Command<T, T>(method: .POST,
path: object.endpoint(.POST),
body: object,
mapper: mapper)
}
private static func updateCommand<T>(_ object: T) -> API.Command<T, T> where T: ParseObject {
let mapper = { (data) -> T in
try ParseCoding.jsonDecoder().decode(UpdateResponse.self, from: data).apply(to: object)
}
return API.Command<T, T>(method: .PUT,
path: object.endpoint,
body: object,
mapper: mapper)
}

As mixed generated+custom objectId is not supported, do you think that such enhancement would be of any use? I believe we can close this topic, otherwise it would make more sense to support the mixed environment also?

@cbaker6
Copy link
Contributor

cbaker6 commented Jul 9, 2021

Do you mean what you posted here, right?

Yes

As mixed generated+custom objectId is not supported, do you think that such enhancement would be of any use?

Posting my answer from here for future reference.

I believe we can close this topic

Feel free to close if you don't have any more questions.

@lsmilek1 lsmilek1 closed this as completed Jul 9, 2021
@mtrezza mtrezza added type:feature New feature or improvement of existing feature and removed type:improvement labels Dec 6, 2021
@parse-github-assistant
Copy link

The label type:feature cannot be used in combination with type:improvement.

@parse-github-assistant parse-github-assistant bot removed the type:feature New feature or improvement of existing feature label Dec 6, 2021
@mtrezza mtrezza added the type:feature New feature or improvement of existing feature label Dec 6, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type:feature New feature or improvement of existing feature
Projects
None yet
Development

No branches or pull requests

3 participants