1.0.0

Changed#

  • The @Query, @Fragment, @PaginationFragment, and @Mutation property wrappers have been replaced with the experimental new versions that used to be called QueryNext and so on. These property wrappers use @StateObject under the hood, which means they are better at keeping track of their underlying state.

    See the API docs for each property wrapper linked above for how to use the new versions, as the API for them has changed, especially for @Query. The Getting Started guide has also been updated to use the new APIs.

  • RecordProxy is now a class-constrained protocol, so methods like setLinkedRecord are no longer mutating and can be used even when the record proxy is stored somewhere read-only like a let variable. This may cause warnings in your code if you storing record proxies in vars and not reassigning them, which can be fixed by changing it to a let.

Removed#

  • Xcode 11 and its corresponding SDK/OS versions (iOS 13, macOS 10.15, etc.) are no longer supported by Relay.swift. If you need to support these OS versions, you'll want to stick with Relay.swift 0.5. There are known bugs in the @Query property wrapper in particular that stem from not having @StateObject available in these older OS versions, which is why this support is being removed.

    The release notes for version 0.3.0 have some more information about why this changed.

0.5.0

Added#

  • When running on iOS 14 or equivalent, various parts of Relay.swift will now emit more debug logging, which can help track the flow of requests and data through the store.

Changed#

  • The cacheConfig parameter passed to a network layer's execute method now includes a force property. When this property is true, the network layer should skip any response cache it may be using and always perform the network request. Note that this does not affect any caching of data that Relay.swift itself may be doing.
  • @RefetchableFragment supports refetching with variables that will change which node the fragment is targeting. If you refetch with variables that point to a different record, the fragment will show data and receive updates for that record rather than the original one it first rendered.
  • When a view using a @Query is disposed, there is now a five-second delay before the query is released and garbage collection is triggered. This was added to workaround an iOS 14 beta bug, though that bug has been fixed in beta 4. Even so, this change is being left in to allow a bit of tolerance for a transition between two views that use the same query.

Fixed#

  • The garbage collector will correctly mark references to records through an inline fragment and not delete them.

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.

0.3.0

Added#

  • A MockEnvironment is now available to allow for writing tests or SwiftUI previews

  • The default ConnectionHandler has new methods that can be used to update paged result sets in an updater function of a mutation. See the ConnectionHandler docs to learn more.

  • Records in the Relay store that are no longer referenced by a visible view will be garbage-collected to free up memory

  • @Mutation's commit function now takes an onComplete block that will be called once the network has completed the mutation

  • All four SwiftUI property wrappers now have an experimental Next variant (e.g. QueryNext) that's only available on iOS 14 or later (and equivalent versions of other Apple platforms). These versions use the new @StateObject wrapper to let SwiftUI manage the storage of their internal state and tie it to the lifetime of the view. This is more correct than the existing property wrappers, which can sometimes get recreated unnecessarily when other view state changes.

    The API for these new wrappers is experimental and may change. In some future release after these platform versions are no longer in beta, support for iOS 13 will be dropped and these new wrappers will replace the existing ones.

    If you're targeting a minimum of iOS 14 (or equivalent), you can use the new wrappers in your app. You may find it handy to make type aliases to the existing names (which will override the names imported from RelaySwiftUI):

import RelaySwiftUI
typealias Fragment = RelaySwiftUI.FragmentNext
typealias PaginationFragment = RelaySwiftUI.PaginationFragmentNext
typealias Query = RelaySwiftUI.QueryNext
typealias Mutation = RelaySwiftUI.MutationNext

Changed#

  • The Swift types generated for reading fragment data now implement Swift's Decodable protocol rather than a custom Readable protocol. The Relay compiler no longer needs to generate init(from: SelectorData) methods for all of these types, and in many cases can rely on Swift itself to synthesize an implementation for init(from: Decoder).

Fixed#

  • Union and interface types can be used and will generate appropriate Swift types
  • optimisticUpdater and updater functions are now supported in mutations. Previously, they could be passed in but they did nothing.
  • The errors in a GraphQL response payload are processed and handled correctly.

0.2.0

Added#

  • environment.fetchQuery method for loading query data without presenting it in a view
  • ToDo list example app

Changed#

  • Changed generic type in Mutator from O to Operation to make extensions clearer
  • Generated Operation and Fragment types now keep variables or keys on instances of the types
  • Cleaned up generating request identifiers

Fixed#

  • Updated the Pokedex and Star Wars example apps to work with the current APIs

Removed#

  • Removed unused AnyEncodable struct

0.1.0

Initial release!