Comments (10)
This ends up feeling like a bit of a hack, but I think I got something working...
let fetch: (ApolloClient, @escaping (Any?, Error?) -> Void) -> Void
let resultType: Any.Type
init<Query: GraphQLQuery>(query: Query) {
fetch = { client, completionHandler in
client.fetch(query: query) { result, error in
completionHandler(result, error)
}
}
resultType = GraphQLResult<Query.Data>.self
}
}
let action = FetchQueryAction(query: HeroNameQuery())
action.fetch(client) { result, error in
switch action.resultType {
case is GraphQLResult<HeroNameQuery.Data>.Type:
let hero = (result as! GraphQLResult<HeroNameQuery.Data>).data?.hero
hero?.name
default:
fatalError("Unknown result type")
}
}
from apollo-ios.
I also tried type erasure to create an AnyGraphQLQuery
but failed due to the same restriction. You obviously cannot stub the static var
from apollo-ios.
I'd love to help, but I'd like to better understand your use case to see if there might be a better way to achieve what you want. What would you want use a AnyGraphQLQuery
for? It seems its use would be limited because it would still need to be parameterized by a specific Data
. (Which isn't a problem for something like AnyIterator
because the type is general, but here Data
is bound to a specific query anyway.)
I've run into the same issue myself in working on upcoming support for dynamic operations and a more flexible network layer. One solution I've been thinking about is to create a GraphQLRequest
protocol with operationDefinition
etc. and use that in the network layer. Because GraphQLOperation
would adopt the protocol, that means you can then assign operations to a variable of type GraphQLRequest
(or collections of that type, etc.).
The default implementation of the protocol on GraphQLOperation
could delegate operationDefinition
etc. to the static version. I'd like to keep information that isn't specific to an operation use static to avoid reinitializing it for each individual query/mutation. (That is more important with some of the new information we're adding for the revised execution algorithm, see #63).
from apollo-ios.
Thank for the comment.
I'll try to explain my use case.
I'm using ReactiveReSwift in other words redux-flow with observables.
In it I want to create a MiddleWare that handles all FetchAction and will fire a success or failure actions depending on what happened.
This way the View layer only needs to fire that action to the Store.
The results will then be handled by the reducers.
The problem I'm currently having is that in order to create a MiddleWare you need to add closures to a MiddleWare class which cannot be parameterized.
Thus I would need to add a case for every query I have in my app.
In order to avoid adding a case for every query AnyGraphQLQuery
would be very welcome.
My code looks something like this.
Middleware<MainState>()
.sideEffect { _, dispatch, action in
switch action {
case let action as FetchAction<VerticalTvGuideQuery>:
GraphQLMiddleWare.fetch(mediaProvider: mediaProvider, dispatch: dispatch, query: action.query, disposeBag: action.disposeBag)
default:
break
}
}
}
private static func fetch<Q: GraphQLQuery>(mediaProvider: MediaProvider, dispatch: @escaping Middleware<MainState>.DispatchFunction, query: Q, disposeBag: DisposeBag) {
mediaProvider.fetch(query).subscribe(
onNext: { data in
let finishAction = FetchFinishAction<Q>(disposeBag:disposeBag, data: data)
dispatch(finishAction)
},
onError: { error in
let failureAction = FetchFailureAction(disposeBag:disposeBag, error: error)
dispatch(failureAction)
}) // onComplete or onDisposed
.addDisposableTo(disposeBag)
}
In the View layer something like this happens.
let fetchAction = FetchAction(disposeBag: self.disposeBag, query: VerticalTvGuideQuery())
store.dispatch(fetchAction)
The success and failure actions will be handles in the Reducer that is responsible for the screen that fired the FetchAction.
from apollo-ios.
I don't think AnyGraphQLQuery
would help here, because it would still have to be parameterized by the Data
type from the particular query it wraps. So in this case, you'd have AnyGraphQLQuery< VerticalTvGuideQuery.Data>
instead of VerticalTvGuideQuery
, and you would run into the same issues.
This is similar to how other type erased wrappers like AnyIterator
work. Although it does hide the specific iterator type, it is still parameterized by the element type. So AnyIterator<String>
is different from AnyIterator<Int>
, and you can't refer to an AnyIterator
without specifying the type.
If you want access to the typed data, the query-specific type information will have to come from somewhere. You may be able to define your own non-generic wrapper, but then you'll have to force cast the result somewhere else in your code in order to use it:
struct FetchQueryAction {
let fetch: (ApolloClient, @escaping (Any?, Error?) -> Void) -> Void
init<Query: GraphQLQuery>(query: Query) {
fetch = { client, completionHandler in
client.fetch(query: query) { result, error in
completionHandler(result, error)
}
}
}
}
let action = FetchQueryAction(query: HeroNameQuery())
action.fetch(client) { result, error in
(result as! GraphQLResult<HeroNameQuery.Data>).data?.hero?.name
}
from apollo-ios.
You are correct as far as I understand it.
But I'm still trying to wrap my head around it so excuse me if I'm asking the obvious.
To come back to the original reason why I wanted AnyGraphQLQuery
: how I can reduce the switch I'm currently using which has (will have) a case for every different query?
e.g.
switch action {
case let action as FetchAction<Query1>:
GraphQLMiddleWare.fetch(mediaProvider: mediaProvider, dispatch: dispatch, query: action.query, disposeBag: action.disposeBag)
case let action as FetchAction<Query2>:
GraphQLMiddleWare.fetch(mediaProvider: mediaProvider, dispatch: dispatch, query: action.query, disposeBag: action.disposeBag)
case let action as FetchAction<Query3>:
GraphQLMiddleWare.fetch(mediaProvider: mediaProvider, dispatch: dispatch, query: action.query, disposeBag: action.disposeBag)
default:
break
}
Also that the results needs to be cast somewhere else is what I'm currently doing with Reducers
.
As they will process the finish or failure result.
For example:
static func createViewModelReducer(mediaProvider: MediaProvider) -> Reducer<VerticalTVGuideState> {
return { action, state in
switch action {
case let action as FetchFinishAction<VerticalTvGuideQuery>:
var state = state
state.queryData = Box(action.data)
state.error = nil
return state
case let action as FetchFailureAction:
var state = state
state.queryData = nil
state.error = Box(action.error)
return state
default:
return state
}
}
}
from apollo-ios.
Then again, if you have to hard code your queries anyway, a cleaner solution might be to use an enum:
enum FetchQueryAction {
case heroName(HeroNameQuery)
case heroAndFriendsNames(HeroAndFriendsNamesQuery)
}
from apollo-ios.
@Nuke- is this still an issue you need help with? I'm trying to clean up old issues and if there's a particular solution you used here, it'd probably be helpful to share with the community before I close this out. Thanks!
from apollo-ios.
@designatednerd no longer an issue. I settled on using a giant switch for all my queries and mutation.
from apollo-ios.
switch
ftw! 😃
from apollo-ios.
Related Issues (20)
- `asXXXXXXX` property on a union never returning `nil` if selection set empty HOT 4
- Cache mutations are somewhat painful to work with HOT 5
- Add PrivacyManifest HOT 1
- Can't Use 2 Different Schemas in same Package HOT 2
- `SelectionSet` Generated Initializers Don't Compile with `self` Parameter HOT 5
- When using apollo-ios-pagination, there is an issue where data is not accurately updated when mutations occur. HOT 1
- Xcode Install CLI fails for versions >=1.8 HOT 11
- Test Issue HOT 1
- Testing author_association
- Process Mutation on a Queue when encountering network errors HOT 2
- WebSocketTransport: detect server side completion message. HOT 4
- Invalid type aliases generated on recursive types HOT 4
- The operation couldn’t be completed. (OSStatus error -9847.) HOT 11
- Cannot convert value of type 'GraphQLNullable<[String]>' to expected argument type '[String?]' HOT 10
- SchemaConfiguration.cacheKeyInfo(for:object:) never called? HOT 5
- Watcher not firing after failure to decode response HOT 2
- Cococapods installation should use download script to fetch CLI instead of using embedded binary
- How to know subscription state HOT 1
- Normalised Cache Not Using Data from Different Query HOT 3
- How @import(module:) works? HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from apollo-ios.