Skip to content

Usage

Apollo client setup

Install Apollo if your app does not already use it:

sh
yarn add @apollo/client graphql

Initialize the client with the device identifier in the headers and wrap your app with ApolloProvider:

tsx
import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client'
import { ApolloProvider } from '@apollo/client/react'
import { deviceIdentifier } from 'trainingkit-reactnative'

const client = new ApolloClient({
  link: new HttpLink({
    uri: 'https://your-graphql-endpoint/graphql',
    headers: {
      'X-TrainingKit-Device': deviceIdentifier(),
    },
  }),
  cache: new InMemoryCache(),
})

export function App() {
  return (
    <ApolloProvider client={client}>
      {/* Your navigation tree */}
    </ApolloProvider>
  )
}

The two-step flow

Launching a workout always takes two GraphQL calls:

  1. List query — returns lightweight workout previews (id, name, format, …) to display a catalog. It does not include trainingKitToken.
  2. Session content query — fetches the full session payload for one workout by id, including trainingKitToken. This is the object you pass to launchWorkout.

Fetch the full session lazily, only when the user selects a workout, so you do not request a launch token for every item in the list.

Listing workouts

The GraphQL documents depend on your generated schema. The list query should return workout preview items that include at minimum: id, name, duration, picture, and format.

format values are CLASSIC (classic guided workout) and PLAY (video workout). In the demo's generated types these map to the WorkoutFormat.Classic and WorkoutFormat.Play enum members used below.

tsx
import { useQuery } from '@apollo/client/react'
import { Pressable, SectionList, Text } from 'react-native'
import { GetSessionsDocument, WorkoutFormat } from '../graphql/types'

export function SessionsScreen({ onSelect }: { onSelect: (id: string) => void }) {
  const { data, loading, error } = useQuery(GetSessionsDocument)

  if (loading) return <Text>Loading...</Text>
  if (error) return <Text>{error.message}</Text>

  const sections = [
    {
      title: 'Classic workouts',
      data: data?.publicWorkouts.edges
        .filter(edge => edge.node.format === WorkoutFormat.Classic)
        .map(edge => edge.node) ?? [],
    },
    {
      title: 'Video workouts',
      data: data?.publicWorkouts.edges
        .filter(edge => edge.node.format === WorkoutFormat.Play)
        .map(edge => edge.node) ?? [],
    },
  ]

  return (
    <SectionList
      sections={sections}
      keyExtractor={item => item.id}
      renderSectionHeader={({ section }) => <Text>{section.title}</Text>}
      renderItem={({ item }) => (
        <Pressable onPress={() => onSelect(item.id)}>
          <Text>{item.name}</Text>
        </Pressable>
      )}
    />
  )
}

Launching a workout

To launch a workout, fetch the full session payload first. The full session response must include trainingKitToken.

tsx
import { useLazyQuery } from '@apollo/client/react'
import { Alert } from 'react-native'
import { launchWorkout } from 'trainingkit-reactnative'
import { GetSessionContentDocument } from '../graphql/types'

export function useWorkoutLauncher() {
  const [fetchSessionContent] = useLazyQuery(GetSessionContentDocument)

  return async function launchWorkoutById(id: string) {
    try {
      const { data } = await fetchSessionContent({ variables: { id } })
      const session = data?.publicWorkoutSession

      if (!session) {
        throw new Error('Workout session not found')
      }

      launchWorkout(session)
    } catch (error) {
      Alert.alert(
        'Workout error',
        error instanceof Error ? error.message : String(error),
      )
    }
  }
}

launchWorkout determines the workout kind from the session and dispatches to the correct native SDK:

  • __typename === 'WorkoutBlockSession' or format === 'CLASSIC' → classic workout
  • __typename === 'WorkoutVideoSession' or format === 'PLAY' → video workout

If neither condition matches, launchWorkout throws with the unrecognized type.

GraphQL contract

The package does not own the Apollo client or the GraphQL schema. Your application is responsible for:

  1. Sending X-TrainingKit-Device with every request (see Authentication).
  2. Querying the workout list and retrieving a preview item.
  3. Fetching the full session payload (including trainingKitToken) before calling launchWorkout.

The session object passed to launchWorkout must conform to:

ts
type TrainingKitSession = {
  __typename?: string
  format?: string
  trainingKitToken: string
  [key: string]: unknown
}

Any additional fields from the GraphQL response are preserved and forwarded to the native SDK as part of the JSON payload.