github apollographql/apollo-ios 2.5.0

4 hours ago

New

  • Support @fieldPolicy key paths through a list of input objects (#1097): A keyArgs path can now pass through one list level of input objects, such as keyArgs: "inputs.id" on heroes(inputs: [HeroInput!]!): [Hero!]!, and produces one cache key per input object (Hero:1, Hero:2, …). Previously, codegen rejected these paths and the runtime resolved them to no keys. Paths with a second list, or with more than one list-valued key argument, are still rejected with a descriptive error.

Improvement

  • Fail the request when a GraphQLInterceptor does not call next (#1074): An interceptor that returned its own results without calling next left the request chain without a final request, which crashed on a force unwrap — even for mutations, subscriptions, and requests that never write to the cache. The chain now fails the request with RequestChain.Error.interceptorDidNotCallNext, naming the offending interceptor and operation. Calling next remains required; skipping it would silently skip fetchBehavior and every later interceptor. Fixes #3654.

Fixed

  • Resolve field policy cache keys to the records they name (#1091): A key produced by a field policy (@fieldPolicy or a FieldPolicy.Provider) was looked up as a field on the parent record, which the normalizer never writes. A policy read of a single object always missed, and a policy read of a list dropped every key and succeeded with [], so .cacheFirst returned an empty list without making a network request. The key now resolves to the keyed record like any other cache reference, and a key whose record isn't cached is a cache miss. Fixes #3659. Thank you to @drewmclean for the contribution.
  • Read stored field values before applying field policies (#1098): A field with a field policy was always read from the record its policy key named, never from the value a network fetch stored for the field's arguments. When the policy key and the stored record's key differed, such as a type policy keyed by id alongside a field policy keyed by name, every read missed and went to the network, even right after fetching the same arguments. The stored value is now read first, and the field policy applies when none is stored.
  • Fix AsyncReadWriteLock allowing two writers to run at once (#1093): When a writer finished, the readers woken from the queue counted themselves only after re-entering the actor. A higher-priority reader could finish before a lower-priority one resumed, dropping the running count to zero and waking the next writer while a reader was still active. Reads and writes could then overlap, which crashed in ApolloStore.unsubscribe through concurrent mutation of its subscribers dictionary. Woken readers are now counted before they are resumed. Thank you to @pixelmatrix for the contribution.
  • Preserve arrival order in AsyncReadWriteLock (#1096): A reader arriving while a read batch was queued ahead of a writer joined that batch and ran before the waiting writer. New readers now join the last queued item, or start a new batch behind a writer, so a stream of readers can no longer pass queued writers. Reads and writes never overlapped in this case. Thank you to @carllindberg for the review note.
  • Ignore stale WebSocketTransport receive loop events after pause()/resume() (#1089): A resume() landing between a receive loop's "is this connection current" check and its disconnect handling let the old loop handle its disconnection against the new connection. A subscription could then be finished as .completed instead of being re-subscribed on the new connection. Thank you to @3redrubies for the contribution.
  • Create WebSocketTransport tasks only when connecting (#1102): The transport created its URLSessionWebSocketTask in init but did not resume it until the first subscription or resume(). URLSession measures timeoutIntervalForResource (7 days by default) from task creation, so a process that stayed alive for a week before its first subscription crashed inside CFNetwork on the first send or receive. Each task is now created immediately before it is resumed. As a result, WebSocketURLSession.webSocketTask(with:) is called at connect time rather than in init, and header and connecting-payload updates made before the first connection now apply to it. Thank you to @3redrubies for the contribution.
  • Fix codegen pruning of files written through symlinked paths (#1100): When the output directory was reached through a symlink (for example /tmp → /private/tmp), the schema module file was written with one path spelling and found by pruning with another, so it was pruned right after being generated on the second run. Paths are now normalized in one place, resolving symlinks in the containing directory. Builds on #1090. Thank you to @carllindberg for the contribution.
  • Honor Glob exclude patterns that don't contain a globstar (#1101): An exclude such as !./Legacy/Old.graphql or !/abs/path/file.graphql lost its leading ! and became an include path that matched nothing, so the exclusion was silently ignored. Only the ** form worked. The ! is now kept for these patterns.
  • Remove a stray parenthesis from GraphQLExecutionError.errorDescription (#1103): The description rendered as Error at path "a.b)": … because of an extra ) after the path.

Don't miss a new apollo-ios release

NewReleases is sending notifications on new releases.