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

Enable Authenticator.response(for:) to return a generic data type #22

Open
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

fritzt0
Copy link

@fritzt0 fritzt0 commented Oct 28, 2024

First of all, I would like to thank you for providing this well-structured library. It is a joy to use. I use it primarily to control access to a medical records research server secured with OpenID Connect. These records are small, so the provided Authenticator.response(for:) method that returns a Data object is not a problem even for the limited memory on iPhones.

However, I now would like to use the library to access and transfer much larger data sets as well, and using Data as data-type to transfer a single entity is a problem. Streaming would be a much better choice (e.g. AsyncBytes or a file interface). To enable this, I tried to generalize the return type of Authenticator.response(for:) and created a proof-of-concept implementation. It works well, but I'm sure the implementation could be improved.

Would you be interested in adding a more generalized version of Authenticator.response(for:) to the library?

And an additional question: How would you go to provide a convenience interface for data uploading?

@mattmassicotte
Copy link
Contributor

No thank you for identifying (and addressing!) this problem!

You’ve caught me at a bad time and I’m going to be a little slow looking. But in the mean time, if you want to try this against the swift6 branch that could be interesting. Had to make a few potentially-invasive changes…

@fritzt0
Copy link
Author

fritzt0 commented Oct 28, 2024

Take your time, there is a solution for the time being. I'll have a look at the Swift 6 branch.

@fritzt0
Copy link
Author

fritzt0 commented Oct 29, 2024

The swift6 branch is even better, fewer concurrency issues. I've rebased my changes onto the swift 6 branch (in a new branch) as well but I've no idea how to add it to this thread or if a new pull request is required.

Copy link
Contributor

@mattmassicotte mattmassicotte left a comment

Choose a reason for hiding this comment

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

Thanks again for doing this. A few questions!

I also think it would be great to also change the target branch to swift6. Is that possible without making a new PR?

let session = URLSession(configuration: .default)

return session.responseProvider
}()
Copy link
Contributor

Choose a reason for hiding this comment

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

Was this moved from Authentication.defaultResponseProvider?

Copy link
Author

@fritzt0 fritzt0 Nov 6, 2024

Choose a reason for hiding this comment

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

Unfortunately, yes. I would be happy to hear of a way to avoid this. Currently, Swift does not support statically stored properties in generic types. I know this would be source breaking. Perhaps using a computed, non-static property could mitigate this problem now that you have also deprecated its use? (But this would require an instance of Authentication.)

@@ -5,6 +5,7 @@ import Foundation
/// This is used to abstract the actual networking system from the underlying authentication
/// mechanism.
public typealias URLResponseProvider = (URLRequest) async throws -> (Data, URLResponse)
public typealias URLUserDataProvider<UserDataType: Sendable> = (URLRequest) async throws -> (UserDataType, URLResponse)
Copy link
Contributor

Choose a reason for hiding this comment

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

You don't think "Response" was the right term?

Copy link
Author

@fritzt0 fritzt0 Nov 6, 2024

Choose a reason for hiding this comment

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

I've been thinking about this for a while, and also imagined a future use where uploading data to OAuth protected servers can be supported. To minimize the impact on the existing API, I would suggest having two providers (which could be the same in the simple case). One used exclusively for authentication purposes and another for the user data transfer part. The former doesn't need to be generic, while the latter should support URLs, dates, and other types. An upload function (similar to Authenticator.response(for:)) would probably also benefit from a different name.

I'm fine with "Response", I was just thinking of another name for the general data type that could be used for downloading and uploading. In addition. A separate user data function would also provide more freedom in handling the user data, e.g. caching strategies, etc.

@fritzt0
Copy link
Author

fritzt0 commented Nov 6, 2024

Thank you for your questions. Maybe I'm missing something, but I don't see any way to addendum to this pull request from another branch. I'll just use my main branch on top of your swift6 branch for the changes.

@fritzt0
Copy link
Author

fritzt0 commented Nov 6, 2024

I had to force push my swift6 branch to main, thus the changes in main are now against your swift6 branch.

@mattmassicotte
Copy link
Contributor

Ok you might have thought that I forgot about this but I definitely do not. In fact, I've been thinking about it all week. In fact, I've also being using this library myself a little for a side project again.

A problem I'm worried about is the need for two authenticators for different data types. Do you have any thoughts on something like this:

struct URLResponseProviders {
    let dataProvider: (URLRequest) async throws -> (Data, URLResponse)
    let asyncBytesProvider: (URLRequest) async throws -> (URLSession.AsyncBytes, URLResponse)
}

I kind of don't like typing the URLSession type directly to this, but I'm unsure how to get around this. Maybe a custom wrapper sequence type is necessary?

I'd just really like to be able to have one single authenticator instance that can do both of these things. And I think that may be important at runtime to correctly handle token refresh.

What do you think? (and thanks again for all your work here, I know its getting complex)

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.

2 participants