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

Add better default initializers for GraphQLNullable<Wrapped> #2729

Closed
PatrickDanino opened this issue Dec 13, 2022 · 16 comments
Closed

Add better default initializers for GraphQLNullable<Wrapped> #2729

PatrickDanino opened this issue Dec 13, 2022 · 16 comments
Labels
feature New addition or enhancement to existing solutions

Comments

@PatrickDanino
Copy link

Use case

I recently completed the porting of our model and network layers from v0.x to v1.0. My general feedback is that this was a lot more painful than it needed to be.

Handling nullable types is now essentially broken by design. Even if you wanted to add the concept of .none versus .null (which is in itself confusing and poorly documented), not supporting nullable values out of the box (such as String?) will force every client of your SDK to either hack something together, write unnecessarily verbose code, potentially pick the wrong option between .none and .nil, or all of the above. It was also exceedingly difficult to get array types to work

Another big pain point was forcing every query parameter to specify a value, likely for the same reason. It seems like this should have been configurable from the code generator, where you could either pick one or the other as the default and handle the potentially few exceptions rather than require lots of boilerplate code for every query.

Describe the solution you'd like

In case it helps others, I was able to at least simplify some of our code by writing an extension like this one:

extension GraphQLNullable {
    /// Initializes GraphQLNullable using a nullable value
    /// - Parameters:
    ///   - object: The value to use if not nil
    ///   - nilValue: The default to use if the value is nil.
    ///   See ``GraphQLNullable`` for more info.
    @inlinable public init(_ object: Wrapped?, nilValue: GraphQLNullable = .none) {
        if let object {
            self = .some(object)
        } else {
            self = nilValue
        }
    }
}
@PatrickDanino PatrickDanino added the feature New addition or enhancement to existing solutions label Dec 13, 2022
@Dimillian
Copy link

Dimillian commented Dec 21, 2022

Yes please. Current implementation is a disaster for upgrading to 1.X. We do have a beautiful Swift API around everything, with you know, Swift optionals. Why would we need GraphQLNullable?
It even make non optional retrieval .value optional where the field is clearly defined as non optional.
I really don't understand the design process behind this.

Please just use Swift optional and delete GraphQLNullable

Thanks.

@ZsoltMolnarrr
Copy link

Totally agree the current API might have been a good idea on paper, but in a sizeable project where enums and optionals are frequently nested this is painful to use.

@erneestoc
Copy link

  • 1 here 👍🏻

@tahirmt
Copy link
Contributor

tahirmt commented Mar 11, 2023

There is a very clear distinction between a null value and nil for Apollo. Sometimes the Apollo backend would require us to send a null value and sometimes not at all if the value doesn't exist. While I understand how the upgrade is painful, this change has been a very welcome change for us. This has also been a feature of the android side and was missing from iOS for a while.

@JoeFerrucci
Copy link

Before v1.0, we were able to initialize any generated model directly using the default initializer (e.g., from a struct).
Now, some models are SelectionSets and the properties have no setters and the provided initializers are verbose and ugly.; Unlike the InputObject type where it provides setters for each property, making it easy to manually construct the models.

How can we generate setters for every generated model?

@calvincestari
Copy link
Member

calvincestari commented Mar 21, 2023

How can we generate setters for every generated model?

@JoeFerrucci, you can track the release/1.1 branch where work is being done to enable selection set initializers. The work is in progress at the moment so that branch may not build.

@dafurman
Copy link

dafurman commented Mar 29, 2023

To those who it may help, I've created some extension properties to make working with GraphQLNullable a bit easier with property chaining, letting you quickly adjust pre Apollo 1.0 code with syntax like "myString".gqlNullable, where you could have used a String directly before.
https://gist.github.com/dafurman/faf04069a8c5ed76354b9eab9c1b3328

@BrentMifsud
Copy link

BrentMifsud commented Apr 6, 2023

I feel like a lot of the 1.0 release has been quite cumbersome to work with like this. For example the GraphQLEnum type is just as awkward to use as the GraphQLNullable.

Also the removal of initializers from fragments makes mocking and testing overly complicated (I realize this is now fixed in 1.1.0, but ive had no luck getting the new inits to generate).

We have been at it for about a week now trying to migrate from 0.53.0 and it's been extremely painful so far (about 500 compile errors to work through for us).

That being said, here is our extension on GraphQLNullable to make it a bit easier to use with optional types. It might help you. Note this assumes that you want to explicitly encode null if you pass in an optional value and it happens to be nil:

import ApolloAPI

extension GraphQLNullable {
    init(optionalValue value: Wrapped?) {
        if let value {
            self = .some(value)
        } else {
            self = .null
        }
    }
}

@JoeFerrucci
Copy link

@BrentMifsud, you might like this thread: #2883

@BrentMifsud
Copy link

@JoeFerrucci unfortunately the new config option for generating initializers isn't working for me. There doesn't seem to be any difference in what gets generated regardless if I add that config option or not 🤷🏻‍♂️

Im currently on apollo 1.1.1

@JoeFerrucci
Copy link

lets see your config.

@BrentMifsud
Copy link

lets see your config.

got it figured out. I needed to move that new config property under options.

@alessdiimperio
Copy link

Thought i'd throw in my take aswell as it allows for imo better readability.

extension GraphQLNullable {
static func optional(_ value: Wrapped?, defaultNil: GraphQLNullable = .none) -> GraphQLNullable {
        guard let value else { return defaultNil }
        return .some(value)
    }
    }

Current example above yields:
Query(param: GraphQLNullable(someOptionalValue))
or
Query(param: .init(someOptionalValue)

My take imo allows for a slight readability improvement.

Query(param: .optional(someOptionalValue))

@Mordil
Copy link
Contributor

Mordil commented Aug 10, 2023

In practice, I definitely haven't been seeing as much value in the GraphQLEnum vs the GraphQLNullable type for existing apps. I bet the value proposition is much higher for greenfield projects, or smaller projects with few enums.

If Swift had the ability to extend Enum literals like we could with nil, I'm sure this value would be highly alleviated and more seamless

This is because in existing apps, we were able to use the raw enum as a plain enum everywhere, with a "natural" default of __unknown for values, and when upgrading we either had to decide our own default based on the values provided, or convert all of our types to Optionals, which got cumbersome to propagate through our codebase

@AnthonyMDev
Copy link
Contributor

This issue has been discussed exhaustively. We understand the cumbersome nature of GraphQLNullable, but in order to ensure fully correct code that behaves as the developer intends it, this has to exist as it is. You are free to implement your own convenience extensions in your projects, but because we can't know if you want to default to null, or nil, we can't add them to the core SDK.

@AnthonyMDev AnthonyMDev closed this as not planned Won't fix, can't repro, duplicate, stale May 17, 2024
Copy link
Contributor

Do you have any feedback for the maintainers? Please tell us by taking a one-minute survey. Your responses will help us understand Apollo iOS usage and allow us to serve you better.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New addition or enhancement to existing solutions
Projects
None yet
Development

No branches or pull requests