0.4.0

Added#

  • Generated result data types with an id: String property are now generated with a conformance to Identifiable, which makes them easier to use in List and ForEach views.
  • Generated structs for @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.
  • Generated query and mutation types now include some convenience extensions to make it easier to work with their variables:
    • The initializer for the operation can take the variables directly instead of having to initialize a variables struct. For example, instead of UserDetailsQuery(variables: .init(userID: "123")), you can write UserDetailsQuery(userID: "123")
    • The 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")
  • The 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.
  • Queries can use two new fetch policies: .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.
  • The 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.

Changed#

  • All generated types and methods are now public (previously they had the default internal access). This makes it possible to keep your generated code in a different module like a SwiftPM package.

  • Matching 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 { /* ... */ }
}

Fixed#

  • The garbage collector will now only collect records on the final release of a query (instead of on every release).
  • A retain cycle between RecordSourceProxy and its RecordProxy instances has been fixed.