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

Ability to send null through Alamofire PATCH requests #3631

Closed
JKnight3517 opened this issue Jul 12, 2022 · 10 comments
Closed

Ability to send null through Alamofire PATCH requests #3631

JKnight3517 opened this issue Jul 12, 2022 · 10 comments
Assignees

Comments

@JKnight3517
Copy link

JKnight3517 commented Jul 12, 2022

Problem

Updating objects on our server with nil values (for instance when you want to null a field in a database) does not work currently with Alamofire. This is an important feature in development espeically when using PATCH endpoints where the absence of a field does not mean that the field is null.

Description and attempted solutions

I have tried a few ways to get this functionality as expected:

  1. I have tried just sending nil values through a dictionary which was suggested to work in this closed issue. However that does not work, as you can see the URLEncodedFormEncoder does not accept nil values
    Screen Shot 2022-07-12 at 11 53 47 AM

  2. I have also tried to create a property wrapper that encodes nil via jSONEncoder and pass that through to Alamofire. This results in sending "<null>" through the API, which is not the desired result (we just want the null keyowrd, not a string)

@propertyWrapper
public struct NullCodable<T> {
    public var wrappedValue: T?
    
    public init(wrappedValue: T?) {
        self.wrappedValue = wrappedValue
    }
}
extension NullCodable: Encodable where T: Encodable {
    public func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch wrappedValue {
        case .some(let value): try container.encode(value)
        case .none: try container.encodeNil()
        }
    }
}
  1. I have found a stackOverflow post that discusses how to potentially use a patch endpoint with Alamorfire here. However, this method is not realistic for complex applications where network requests are abstracted, and besides creating the http body manually is more of a hacky solution rather than a safer one that could be developed as part of the Alamofire pod.

Feature Request

Allow requests using the http method PATCH to send the null keyword as a value for parameters.

Value to Alamofire

This an expected behavior of PATCH endpoints and would make Alamofire easier to use for developers. Right now developers are forced to either have backend developers change to PUT endpoints, or when they are unable to change that, the iOS developers must spend many hours trying to implement a custom solution for PATCH endpoints.

Example of desired output when sending nil values through an Alamofire patch endpoint:

Screen Shot 2022-07-12 at 2 17 42 PM

@jshier
Copy link
Contributor

jshier commented Jul 12, 2022

Using URL encoding for body is pretty rare, which is why this feature wasn't built initially. Are you sure you can't use JSON?

In any event, this looks doable, but I'll investigate more later.

@jshier jshier self-assigned this Jul 12, 2022
@jshier
Copy link
Contributor

jshier commented Jul 12, 2022

Another question is how to handle nil in the first place. Vapor's URLEncodedFormEncoder just skips nil values altogether. I'm tempted to go that direction rather than sending explicit null values, both because it's easier and because it would be better to match an existing implementation. If null values are required that may also then require the addition of a NilEncodingStrategy to drop the values or replace them with null.

@jshier
Copy link
Contributor

jshier commented Jul 16, 2022

I've added a NilEncoding type on the feature/nil-encoding branch, feel free to give it a try. If you need null values, create your own instance of the parameter encoding configured that way: URLEncodedFormParameterEncoder(encoder: .init(nilEncoding: .null)).

@jshier
Copy link
Contributor

jshier commented Jul 16, 2022

Looking at it again, it definitely looks like your example output is JSON, not a form encoding. That encoding is up to JSONEncoder which I don't believe offers a configuration that will return null for nil values. But pretty much all JSON backends can accept either, so your backend should be able to interpret missing keys as nulls easily.

If that behavior is absolutely required you can use of other alternate JSON encoders that may have that ability. Just make it conform to Alamofire's DataEncoder protocol and you can use it as a parameter encoder.

@JKnight3517
Copy link
Author

Hey jshier, apologies i was out for a few days. Yes I think you're right, the heart of this problem was that our networking layer on iOS side was restricting everything to URLencoding before getting to Alamofire. We were finally able to detangle it and test out passing to JSONEncoding and that worked as expected. i think your above comment about copying Vapor's behavior is a good one and would make sense. Thanks for working on this and responding so quickly. Did you want me to close this request or did you want to do that?

@jshier
Copy link
Contributor

jshier commented Jul 18, 2022

I'll do it once I decide whether or not to ship the NilEncoding work.

@jmonroe
Copy link

jmonroe commented Feb 7, 2023

I would like to throw a +1 on this. Not being able to use Encodable types to represent requests which might have optional values seems like something that should be remedied. Skipping nil keys seems like better default behavior and aligns with how optional keys are handled by the JSON encoder.

Has anyone come up with a good workaround for this? I am considering the following:

  1. Avoiding using Encodable types and instead use Parameters
  2. Duplicating the URLEncodedFormParameterEncoder from the previously mentioned branch and embedding it in our codebase.

@jshier
Copy link
Contributor

jshier commented Feb 7, 2023

@jmonroe You can use the feature/nil-encoding branch to use URLEncodedFormParameterEncoder with optional values. I'll look into making that a PR, as we should at least be able to skip values.

@jmonroe
Copy link

jmonroe commented Feb 7, 2023

@jshier Thanks, I will look at doing that for now. Thanks for the update. I was hesitating to use the branch without knowing if it was going to move forward or not.
I appreciate all the work that has gone into this!

jshier added a commit that referenced this issue Feb 7, 2023
### Issue Link 🔗
#3631

### Goals ⚽
This PR adds a `NilEncoding` type for `URLEncodedFormParameterEncoder`
so that it can handle optional values.

### Implementation Details 🚧
Like the other encodings, it allows a variety of representations. Unlike
the others, it's implemented as a `struct` so it can more easily be used
with custom encodings.

### Testing Details 🔍
Tests added for each encoding.
@jshier
Copy link
Contributor

jshier commented Feb 7, 2023

NilEncoding has been merged and will go out in the next release.

@jshier jshier closed this as completed Feb 7, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants