cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Announcements
Want to learn some quick and useful tips to make your day easier? Check out how Calvin uses Replay to get feedback from other teams at Dropbox here.

Dropbox API Support & Feedback

Find help with the Dropbox API from other developers.

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Store upload request to a class var and cancel it later

Store upload request to a class var and cancel it later

kacho
Explorer | Level 3

Hi,

 

I'm implementing Dropbox in my latest app. The app needs to do upload, share files and retrieve the shared links.

 

This is done the following way.

1. The app checks if the file is previously uploaded by id

2. The app checks if the id has shared link and if id doesn't goes to step 4

3. The app requests links and then enumerates to find if there is one suitabe. If it's found then the link can be used and all is fine.

4. If there is no shared link the app requests sharing with needed permissions and takes the desired link from the response so the job is finished.

5. If there is no uploaded file with the given id the app makes an upload request and then goes to step 4 as obviously there won't be any shared link.

 

All of this is working fine.

In the app this can be done for a signle file or for multiple files and then I need to have a way to cancel the requests for some or all of the files. I have my own scheme how to do this but I need to store the currently active (processing) requst in a class var so later I can just call cancel() on it but I'm reaaly confused how to do it. I simply can't understand and can't find the declaration type of that var. 

I tried Alamofire.Requst and many other types but really can't figure it out.

 

The documentation is so confusing and badly organised that is not much of a help. So here is my last chance to find some guidances.

 

Thanks

6 Replies 6

Greg-DB
Dropbox Staff
We'll be happy to help with this. Can you share the name and version of the SDK you're using, as well as the relevant code you have so far? Thanks in advance!

kacho
Explorer | Level 3

Hi Greg and thanks for the quiq reply,

 

I'm away now and don't have access to the source but I believe it's the latest version of SwiftyDropbox. 

 

As for the code - it's as I described:

First I get metadata for a given ID. If the ID is present, I list shared links and iterate to find what I'm looking for. If I find it the whole process completes as I olnly need the link (if the file is uploaded already).

If I can't find the link or the file doesn't have any shared links, I insert new share and from the response I get the link I need and all is done as in the previous case - it means the file is uploaded and I create a link and get it.

If, however, the file is not present in the system by the ID I initiate an upload either by URL (input) or by uploading chunks using SessionStart, SessionAppendV2 and SessionFinish.

After the whole upload is completed I, again, do a create share request to obtain a shred link and I'm done if no errors.

 

I can't post now any code but it's a common pattern I think (at least what I coul'd figure out from the SDK as I personally think it's quite confusing and the docs are really misleading but I don't want to criticise your work or to be rude).

 

As you can see all of those steps are diffrent requests and I want to be able to cancel eventually each and every one of them at some point - if the user decides not to upload or to dismiss sharing and so on. Every request is a differen type, from what I can see in the documentation.

The problem is I really can't understand if I can make a stored variable like, let's say:

var dropboxRequest: SomeDropboxType?

and when any of the requests start assign it to this var.

Later when the user wants to cancel just to call 

dropboxRequest.cancel()

to stop proceeding the current task. I have, as I mentioned, my own way of tracking the following tasks regarding the same ID so stopping the current executing request will be enough I think.

 

My real problem is I don't get your logic behind the API, can't undestand you documentation so I just can't figure out such a simple think as declaring a variable to store the current request sent to Dropbox.

 

Thanks

 

Greg-DB
Dropbox Staff

Thanks for elaborating! Right now you can do this explicitly, like:

var request: UploadRequest<Files.UploadSessionStartResultSerializer, VoidSerializer>?
self.request = DropboxClientsManager.authorizedClient!.files.uploadSessionStart(input: Data()).response(completionHandler: { (result, error) in
    if ((result) != nil) {
        print(result!)
    } else if (error != nil) {
        print(error!)
    }
})

self.request!.cancel()

You can get the exact return type for any method from the documentation, e.g., for uploadSessionStart. (Scroll to the right in "Declaration".)

 

 

Unfortunately, the SDK isn't set up in a such a way that would let you do this in a more general way, but I'll be sure to pass this along as a feature request. 

kacho
Explorer | Level 3

Hi again,

 

I tried this before I decided to post here and unfortunatelly it's not working. The compiler complains:

 

 

1. Cannot specialize non-generic type 'UploadRequest' 
(and wants me to delete <Files.UploadSessionStartResultSerializer, VoidSerializer>) 2. 'UploadRequest' is ambiguous for type lookup in this context ! Found this candidate (Alamofire.UploadRequest) ! Found this candidate (SwiftyDropbox.UploadRequest)

 

 

One other note - this way if I want to cancel a task as described earlier (get metadata, list shared links, create new if necessary or even upload before creating the link) I have to store each and every request in a separate var to be able to cancel all the task regarding the same object - file in this case. ANd the upload as I mentioned could be a regular 

upload(path:mode:autorename:clientModified:mute:input:)

 

or the chunk model using 

 

uploadSessionStart(_:input:)
uploadSessionAppendV2(cursor:close:input:)
uploadSessionFinish(cursor:commit:input:)

which all have different declaration types.

 

 

So for example if the user starts the whole process and, let's say the file has been uploaded earlier so the app starts a request to get the links. Imagine that the user uses a very poor connection and this process takes quite a time.

So I won't be able to cancel this unless I have another var to store this request. And the UI would look like unresponsive if I just wait for it to finish.

Another inconvenience is that, even if I store all the requests in separate vars, I need to figure out which one is currently active and their order will not be the same everytime. 

And all of them are actually just HTTP requests that has one common method - cancel(), which purpose is to cancel the request. Couldn't they conform to a protocol which is the most common practice.

 

I know it's an open source project and I can go digg and change the code to make it work as I think is normal, but... You release an SDK which purpose is to serve people to integrate your service into their apps and facilitate them. I really don't think I should take a SDK and instead of finishing the job I'd start improving it.

 

Thanks again

Greg-DB
Dropbox Staff

Yes, right now unfortunately there isn't a generalizable way to do this, e.g., with a protocol. You'd have to store each necessary type and keep track of them. I've filed this with the team as a feature request for a good way to do this though.

 

Anyway, the code I shared did work for me. Based on the error you're getting, it sounds like you're importing both SwiftyDropbox and Alamofire in the same file. (They both have their own "UploadRequest", so the declaration becomes ambiguous.)

 

You can fix that by either:

  • removing your Alamofire import if you don't actually need it (or refactor it away)
  • use "SwiftyDropbox.UploadRequest" instead, to make is unambigious

 

 

 

kacho
Explorer | Level 3

Hi Greg,

 

Thanks for the tip. I already did that - after logging the type of the var obtained from the request I found this out and I use now the SwiftyDropbox prefix and the compiler is happy. I cannot remove Alamofire as it's needed for other functionality of that class so this would be the way I'll go.

 

Just in case other people need something similar to my casse... I declared a variable:

 

Spoiler

var request: Any?

Then come the "ugly" part in the code - when I want to cancel the job I check through all the possible kinds of Dropbox requests:

 

 

if let req = request as? SwiftyDropbox.UploadRequest<SwiftyDropbox.Files.UploadSessionStartResultSerializer, SwiftyDropbox.VoidSerializer> {
	req.cancel()
	request = nil
} else if let req = request as? SwiftyDropbox.UploadRequest<SwiftyDropbox.VoidSerializer, SwiftyDropbox.Files.UploadSessionLookupErrorSerializer> {
	req.cancel()
	request = nil
} else if let req = request as? SwiftyDropbox.UploadRequest<SwiftyDropbox.Files.FileMetadataSerializer, SwiftyDropbox.Files.UploadSessionFinishErrorSerializer> {
	req.cancel()
	request = nil
} else if let req = request as? SwiftyDropbox.UploadRequest<SwiftyDropbox.Files.FileMetadataSerializer, SwiftyDropbox.Files.UploadErrorSerializer> {
	req.cancel()
	request = nil
} else if let req = request as? SwiftyDropbox.RpcRequest<SwiftyDropbox.Sharing.ListSharedLinksResultSerializer, SwiftyDropbox.Sharing.ListSharedLinksErrorSerializer> {
	req.cancel()
	request = nil
}...

and so on. Otherwise I would have had several different vars to hold each request separatelly.

 

The the next tricky part is to check the errors as this will lead to one. Actually the error handling with SwiftyDropbox is a tricky part itself because there are a lot of things that you wouldn't expect to return as errors but they do and also the way the errors are constructed and retrieved in code.

For expample, as I mentioned earlier, I need the shared link for a certain file and when I make a call to create a shared link (createSharedLinkWithSettings) I expect to have a link in the response. However if the file has already been shared before I get an error instead of the link itself which took me a while to understand how to deal with (I still don't get the logic but just have to accept it I guess).

 

So instead of just passing the link in the response I have this error and that means not really an error (as it's not a big deal - the file is there and it's shared and the link exists... It's just that you have to make another request to retrieve all the shares (whether you need them or not), then iterate, check, look etc. and if you find what you're looking for it's fine. If not... well... another request to create the link again. Simple enough.

 

So for the errors... it's perfectly fine to check for them. The great part is how they are created and presented to you. For the cancelling thing, you have to check if there is an error, then you need to check the type and if it's clientError then, by casting it as an NSError, you can check it's code to see if it's NSURLErrorCancelled. If this is the case then don't treat like an error (at least in my case because all the job was executed when I make the cancel() call) or do whatever is needed after the requst has been cancelled.

But be careful because if you make an upload requst the error will be CallError<Files.UploadErrorSerializer.ValueType>, if you share file it will be CallError<Sharing.CreateSharedLinkWithSettingsErrorSerializer.ValueType> and so on and so on - again that loooong list of types, subtypes, nested things, wrapped and who knons waht else. Same pattern as for the request - don't expect any unification for easier dealing and cleaner code.

 

Well... for now I think I menaged to make it do what I need.

 

Apart from the SDK structure and logic, it's quite stable and works well. The Dropbox service itself is great so it's worth spending a loooot of time until you understand the SDK, API and how to implement it if you need it in your app and you users would expect and appreciate it.

 

Now I'm able to upload videos from the app sandbox or directly the Camera Roll to the users' Dropbox folders and then share them and retrieve the shared link which was what I needed. It took me a while but...

 

Thanks Greg - during the chats with you I figured out few things that helped me finalise the implementaion.

 

Need more support?
Who's talking

Top contributors to this post

  • User avatar
    kacho Explorer | Level 3
  • User avatar
    Greg-DB Dropbox Staff
What do Dropbox user levels mean?