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
Feature - Request Subclasses and New Progress APIs #1455
Conversation
cc @jshier and @kcharwood for review. |
open static func authorizationHeaderFrom(user: String, password: String) -> [String: String] { | ||
guard let data = "\(user):\(password)".data(using: String.Encoding.utf8) else { return [:] } | ||
/// - returns: A tuple with Authorization header and credential value if encoding succeeds, `nil` otherwise. | ||
open static func authorizationHeaderFrom(user: String, password: String) -> (key: String, value: String)? { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really a part of this PR, but I think the naming here could be better. Perhaps authorizationHeader(withUser:password)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really want to get rid of the with
altogether here. Any factor type methods seem like they don't need it. For example, the expectation(withDescription:)
API in XCTest is now expectation(description:)
. Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The guidelines don't offer much guidance in this case, except to say:
When the first argument forms part of a prepositional phrase, give it an argument label. The argument label should normally begin at the preposition, e.g. x.removeBoxes(havingLength: 12).
I believe with
is the prepostion here. That said, I'm okay with dropping it in most cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in 004b315.
@cnoon Review complete. Not much feedback here, looks great! |
… tuple. In the case that utf8 encoding fails, this method should return `nil`, not an empty dictionary. It’s also strange to use a dictionary to return a single key-value pair in the success case. By switching to an optional tuple, the caller must be more explicit about handling the `nil` case making the call safer.
…uracy. There were several issues with the previous implementation. The main problem was the upload tasks uses the same `Progress` property for the upload and download portions of the request. This could cause all sorts of strange progress reporting behavior. Additionally, there were no APIs that returned the `Progress` object directly and you couldn’t specify what queue the progress closures should be called on. This commit addresses all these limitations. The `downloadProgress` APIs are available on the data, download and upload requests. The `uploadProgress` APIs are only available on upload requests.
db619cc
to
97658eb
Compare
All changes have been addressed @jshier. I'm going to merge this bad boy. |
I download file url is http://10.7.7.100:8080/auth0919.tar.gz progress always 0.0,BTW Other url(not .tar.gz ) is ok |
This PR splits up the
Request
class intoDataRequest
,DownloadRequest
,UploadRequest
andStreamRequest
subclasses.Motivation
There were several reasons why the
Request
class could greatly benefit from being split up.Progress
The first reason is that the progress APIs were stomping on each other and were confusing the users. For example, the upload progress behavior was very ambiguous considering that the same
Progress
instance was used to report both upload progress and then download progress after the upload was complete.Session Manager Duplication
The
feature/adapter-and-retrier
PR added theTaskConvertible
enumeration to theRequest
class. Unfortunately, there is a large amount of duplication between theTaskConvertible
implementation and theDownloadable
,Uploadable
andStreamable
enumerations inside theSessionManager
. Not only is there duplication, but theDownloadable
,Uploadable
andStreamable
enumerations don't really belong in theSessionManager
anyways. They're really the pieces required to construct a concreteRequest
.By splitting up the
Request
into subclasses, we could move theDownloadable
,Uploadable
andStreamable
implementations into their respective subclass and eliminate the duplication with theTaskConvertible
enumeration in theRequest
class.In the previous PR where the
TaskConvertible
enumeration was added, theDownloadable
,Uploadable
andStreamable
enumerationsResponse Serializers
Another awesome reason for splitting up the
Request
class is to associate response serializers with a particular request type. For example, right now you can chain aresponseData
closure onto aRequest
that is backed by aURLSessionDownloadTask
or aULRSessionStreamTask
. However, if you do this, what's the behavior? It currently gets really weird in all these cases when you start to consider resume data.Also, how does one get the destination URL of a download? Great question, right now it is WAY more difficult than it should be. By splitting up the
Request
, we can extend only certain subclasses to support specific response serializers that are tailored to the underlying type of task. These changes will be coming in a future PR.Solution
So now that we have some background as to why these changes would be useful, let's dig into what had to change to make this work.
TaskConvertible
By pulling the
Downloadable
,Uploadable
andStreamable
enumerations into their respective subclasses and adding aRequestable
enumeration to theDataRequest
subclass, I was able to get rid of theTaskConvertible
enumeration and make it a protocol. The protocol now creates aURLSessionTask
from a session, adapter and queue. This allows the subclassed enumerations to construct the tasks directly without having to do this in theSessionManager
. This greatly simplifies theSessionManager.retry
method.Progress
The progress APIs have been completely rebuilt from scratch. You can now monitor
downloadProgress
on data, download and upload requests. You can also monitoruploadProgress
on an upload request. There are variants for both theProgress
object and the broken outInt64
values. Each variant can also call the progress closure on a specified queue.Here's a quick example of how you can use the new APIs.
Download Progress
The download progress is now being tracked in the
DataTaskDelegate
. This allows the data, download and upload requests to leverage it. I debated for a long time as to whether to call thisprogress
ordownloadProgress
. I would rather error on the side of verbosity here I think. I ultimately decided upondownloadProgress
becauseprogress
is REALLY ambiguous when it comes to an upload request.Upload Progress
The upload progress is all managed by the
UploadTaskDelegate
and is only exposed publicly through the upload request subclass. The stream request cannot chaindownloadProgress
oruploadProgress
APIs because it no longer inherits them.Top-Level Stream APIs
I added the top-level stream APIs to
Alamofire.swift
because they were missing. Just wanted to callout that this random change made its way into this PR as well.Summary
These changes vastly improve the
progress
APIs and open up the ability to greatly specialize the available chained methods for each subclass. This will help avoid confusion and will allow for better customization for each request type.