Skip to content

Make URLSession and URLSessionTask more source-compatible with Darwin #2587

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

Merged
merged 1 commit into from
Jan 16, 2020

Conversation

broadwaylamb
Copy link
Contributor

@broadwaylamb broadwaylamb commented Dec 13, 2019

I'd be happy to write tests, but what should we test here?

cc @parkera

Comment on lines +427 to +442

/*
* data task convenience methods. These methods create tasks that
* bypass the normal delegate calls for response and data delivery,
* and provide a simple cancelable asynchronous interface to receiving
* data. Errors will be returned in the NSURLErrorDomain,
* see <Foundation/NSURLError.h>. The delegate, if any, will still be
* called for authentication challenges.
*/
open func dataTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask {
return dataTask(with: _Request(request), behaviour: .dataCompletionHandler(completionHandler))
}

open func dataTask(with url: URL, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask {
return dataTask(with: _Request(url), behaviour: .dataCompletionHandler(completionHandler))
}
Copy link
Contributor Author

@broadwaylamb broadwaylamb Dec 13, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved these from an extension to the class itself. With Darwin Foundation we're able to override these methods in subclasses. Same with downloadTask and uploadTask methods.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SE-0117 strikes again.

I think you can mark each method in the extension with open, and that should reduce the size of this PR and keep changes easier to see.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They were already open, it’s just that we can’t override methods declared in extensions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I didn't double checked my proposal.

I just tested in a small Xcode (Mac) project that reproduces the problem, and I get this error "Overriding non-@objc declarations from extensions is not supported". I tried adding a simple @objc in front of the extension in the Swift framework, and it seems to fix the problem.

Do you think that might be a better solution? I don't mind moving the methods into the main class code, but if someone wrote them as an extension for organization, I would prefer to keep them that one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m afraid @objc won’t work on non-Darwin platforms.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's true. What a strange limitation of extensions. I wonder why open there doesn't generate a warning, if it cannot be overriden anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK this has to do with method dispatch. We can't retroactively add methods to class vtable, unless we use some completely different dispatching mechanism, like message passing in Objective-C. That's why @objc works — it relies on Objective-C runtime.

Comment on lines +373 to +379
@available(*, unavailable, renamed: "getTasksWithCompletionHandler(_:)")
open func getTasksWithCompletionHandler(completionHandler: @escaping ([URLSessionDataTask], [URLSessionUploadTask], [URLSessionDownloadTask]) -> Void) {
getTasksWithCompletionHandler(completionHandler)
}

/* invokes completionHandler with outstanding data, upload and download tasks. */
open func getTasksWithCompletionHandler(completionHandler: @escaping ([URLSessionDataTask], [URLSessionUploadTask], [URLSessionDownloadTask]) -> Void) {
open func getTasksWithCompletionHandler(_ completionHandler: @escaping ([URLSessionDataTask], [URLSessionUploadTask], [URLSessionDownloadTask]) -> Void) {
Copy link
Contributor Author

@broadwaylamb broadwaylamb Dec 13, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Darwin Foundation doesn't use an argument label.

Marked the old version as unavailable instead of deprecated, because in the latter case we have ambiguity if this method is used with trailing closure syntax.

@@ -268,7 +268,7 @@ open class URLSession : NSObject {
}

open private(set) var delegateQueue: OperationQueue
open var delegate: URLSessionDelegate?
open private(set) var delegate: URLSessionDelegate?
Copy link
Contributor Author

@broadwaylamb broadwaylamb Dec 13, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

URLSession.delegate is get-only in Darwin Foundation.

Therefore, we should be able to override it with a get-only property. We can't do this with swift-corelibs-foundation now.

@@ -376,7 +376,7 @@ open class URLSessionTask : NSObject, NSCopying {
/*
* The current state of the task within the session.
*/
open var state: URLSessionTask.State {
open fileprivate(set) var state: URLSessionTask.State {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@broadwaylamb
Copy link
Contributor Author

broadwaylamb commented Jan 6, 2020

Could anyone please verify this patch?

@phausler @parkera @millenomi @drodriguez

Copy link
Contributor

@drodriguez drodriguez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the changes. They look OK and sensible to me. I would still defer to someone in the Apple team to express their opinions, but I would not mind merging these changes in the name of cross-compatibility.

About the missing unit tests, I don't think they are strictly necessary for these changes, but if you want to add them, here are some ideas.

  • I don't think the fileprivate(set) bits can be tested. It is a compilation error. No way to test that with XCTest. Hopefully the tests didn't use the inexistent API.
  • About getTaskWithCompletionHandler, I suppose the tests do not use the one with the parameter name (it is probably all trailing closures). It might not be bad to add or modify one of the tests to use an explicit parameter, instead of the trailing closure.
  • About the data/upload/downloadTask methods, one can check that overriding in subclasses is possible, and that nobody will break that support by adding a subclass and checking that the override is called thru (modify the returned taskDescription and check the modification in the returned value, for example).

But as I said above, I don't think they are strictly necessary.

@swift-ci please test Linux platform

Comment on lines +427 to +442

/*
* data task convenience methods. These methods create tasks that
* bypass the normal delegate calls for response and data delivery,
* and provide a simple cancelable asynchronous interface to receiving
* data. Errors will be returned in the NSURLErrorDomain,
* see <Foundation/NSURLError.h>. The delegate, if any, will still be
* called for authentication challenges.
*/
open func dataTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask {
return dataTask(with: _Request(request), behaviour: .dataCompletionHandler(completionHandler))
}

open func dataTask(with url: URL, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask {
return dataTask(with: _Request(url), behaviour: .dataCompletionHandler(completionHandler))
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SE-0117 strikes again.

I think you can mark each method in the extension with open, and that should reduce the size of this PR and keep changes easier to see.

@drodriguez
Copy link
Contributor

@swift-ci please test

@broadwaylamb
Copy link
Contributor Author

Can anyone take a look and merge this please? @phausler @parkera @millenomi

Copy link
Contributor

@phausler phausler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes look reasonable to me

@drodriguez drodriguez merged commit 12e60f6 into swiftlang:master Jan 16, 2020
@drodriguez
Copy link
Contributor

Merged. You might want to ask for a cherry-pick of this to the latest 5.x.y branch going on for Linux.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants