id: String
property are now generated with a conformance to Identifiable
, which makes them easier to use in List
and ForEach
views.@connection
fields conform to RandomAccessCollection
if they include a selection of edges { node { ... } }
. This means that you can replace something like data.allFilms.edges.map { $0.node }
with just data.allFilms
. See the @PaginationFragment docs for more info.UserDetailsQuery(variables: .init(userID: "123"))
, you can write UserDetailsQuery(userID: "123")
get()
method on the wrapped value for the QueryNext
property wrapper (the beta version of @Query) can also take the query's variables directly. For example, instead of query.get(.init(userID: "123"))
, you can write query.get(userID: "123")
QueryNext
property wrapper supports refetching queries by passing in a fetchKey
parameter to the get()
method. Whenever the fetch key is changed from the last time get()
was called, the query will be refetched. One way to use this is to have a @State
property for the fetch key and have your view change the value (a counter or UUID, perhaps) when it wants to refetch..storeOrNetwork
and .storeOnly
. The former avoids a network request if the data in the local store is complete and valid. The latter always skips the network, and expects the data to be present locally already.RefetchableFragment
property wrapper has been added to RelaySwiftUI
. It supports fragments with a @refetchable
directive in their GraphQL definition. This wrapper is like an ordinary @Fragment but it includes a refetch()
method to refetch its data using a generated refetch query.RecordSource
now conforms to Codable
. This allows a Store's records to be saved and loaded to disk, for instance. Until garbage collection is more configurable, this is probably of limited usefulness.QueryNext
, the FragmentNext
and PaginationFragmentNext
property wrappers don't have a projected value anymore (accessed with the $
prefix). You must now set the key for a fragment property wrapper when it's initialized. This is closer to how Apple's own property wrappers, like @Binding
, work.To make this easier, types that conform to a fragment's Key
protocol now also generate asFragment()
methods to create the FragmentNext
or PaginationFragmentNext
to pass on to a child view. Using this lets you avoid writing initializers for fragment views in many cases.
// Before:
struct MoviesTab: View {
@QueryNext(MoviesTabQuery.self) var movies
var body: some View {
switch movies.get() {
// ...
case .success(let data):
if let data = data {
MoviesList(films: data) // MoviesTabQuery.Data conforms to MoviesList_films_Key
}
}
}
}
struct MoviesList: View {
@PaginationFragmentNext(MoviesList_films.self) var films
init(films: MoviesList_films_Key) {
$films = films
}
var body: some View { /* ... */ }
}
// After:
struct MoviesTab: View {
@QueryNext<MoviesTabQuery> var movies
var body: some View {
switch movies.get() {
// ...
case .success(let data):
if let data = data {
// Use asFragment() to create the PaginationFragmentNext that
// MoviesList expects.
MoviesList(films: data.asFragment())
}
}
}
}
struct MoviesList: View {
@PaginationFragmentNext<MoviesList_films> var films
// The default initializer is fine now because the parent view is passing
// a PaginationFragmentNext instead of a fragment key.
var body: some View { /* ... */ }
}