Use generated iOS SDKs

Firebase Data Connect client SDKs let you call your server-side queries and mutations directly from a Firebase app. You generate a custom client SDK in parallel as you design the schemas, queries and mutations you deploy to your Data Connect service. Then, you integrate methods from this SDK into your client logic.

As we've mentioned elsewhere, it's important to note that Data Connect queries and mutations are not submitted by client code and executed on the server. Instead, when deployed, Data Connect operations are stored on the server like Cloud Functions. This means you need to deploy corresponding client-side changes to avoid breaking existing users (for example, on older app versions).

That's why Data Connect provides you with a developer environment and tooling that lets you prototype your server-deployed schemas, queries and mutations. It also generates client-side SDKs automatically, while you prototype.

When you've iterated updates to your service and client apps, both server- and client-side updates are ready to deploy.

Generate your Swift SDK

As with most Firebase projects, work on your Firebase Data Connect client code takes place in a local project directory. Both the Data Connect VS Code extension and the Firebase CLI are important local tools for generating and managing client code.

SDK generation options are keyed to several entries in the dataconnect.yaml file generated when you initialized your project.

Initialize SDK generation

In your connector.yaml, add your outputDir, package and (for the web SDK) packageJsonDir.
connectorId: "movies"
generate:
  swiftSdk:
    outputDir: "../movies-generated"
    package: "Movies"

outputDir specifies where the generated SDK should output to. If not specified, the connector folder is used as the default output directory.

package specifies the name of the package that will be generated. The generator will create a folder with the name of the package, containing Package.swift and generated code.

observablePublisher (optional) specifies the Observable publisher to use in query refs. Possible values are observableMacro (iOS 17+) and observableObject (pre - iOS 17). The default value, if none is specified, is observableMacro.

Update SDKs while prototyping

If you're prototyping interactively with the Data Connect VS Code extension and its Data Connect emulator, SDK source files are automatically generated and updated while you modify .gql files defining schemas, queries and mutations. This can be a useful feature in hot (re)loading workflows.

In other scenarios, if you're using the Data Connect emulator from the Firebase CLI, you can set a watch on .gql updates and also have SDK sources automatically updated. The CLI gives you more control.

For example, you could set a watch and generate only Swift SDK sources for the moviereviews-postgres connector.

firebase dataconnect:sdk:generate --watch --only moviereviews-kotlin

Generate SDKs for integration and for production releases

In some scenarios, such as preparing project sources to submit for CI tests, you can call the Firebase CLI for a batch update.

In these cases, use firebase dataconnect:sdk:generate.

Set up client code

To set up your client code to use Data Connect and your generated SDK, first follow the standard Firebase setup instructions.

Then, open your app workspace using Xcode.

In the top navigation bar, select File > Add Package Dependencies > Add Local, and choose the folder containing the generated Package.swift source file.

Initialize the Data Connect iOS SDK

Initialize your Data Connect instance using the information you used to set up Data Connect (all available in the Firebase console Data Connect tab).

Getting a connector instance

The code for your connector will be generated by the Data Connect emulator. If your connector name is movies and the package is movies, as specified in connector.yaml, then retrieve the connector object by calling:

let connector = DataConnect.moviesConnector

Running Queries and Mutations

With the connector object, you can run queries and mutations as defined in the GraphQL source code. Suppose your connector has these operations defined:

mutation createMovie($title: String!, $releaseYear: Int!, $genre: String!, $rating: Int!) {
  movie_insert(data: {
    title: $title
    releaseYear: $releaseYear
    genre: $genre
    rating: $rating
  })
}

query getMovieByKey($key: Movie_Key!) {
  movie(key: $key) { id title }
}

query listMoviesByGenre($genre: String!) {
  movies(where: {genre: {eq: $genre}}) {
    id
    title
  }
}

You can then create a movie as follows:

let mutationResult = try await connector.createMovieMutation.execute(
  title: "Empire Strikes Back",
  releaseYear: 1980,
  genre: "Sci-Fi",
  rating: 5)

print("Movie ID: \(mutationResult.data.movie_insert.id)")

To retrieve a movie, you will use a query reference. All query refs are Observable publishers. Depending on the configured publisher (see connector.yaml), they either support the @Observable macro (iOS 17+) or implement the ObservableObject protocol. The default, if none is specified, is the @Observable macro supported on iOS 17+.

In a SwiftUI view, you can bind the query results using the published data variable of the query ref and call the query's execute() method to update the data. The data variable will match the shape of data that was defined in your GQL query definition.

All retrieved results comply with the Decodable protocol. If you included the object's primary key in your GQL fetch, the objects are also Identifiable, allowing you to use them in iterators.

struct ListMovieView: View {
    @StateObject private var queryRef = connector.listMoviesByGenreQuery.ref(genre: "Sci-Fi")
    var body: some View {
        VStack {
            Button {
                Task {
                    do {
                        try await refresh()
                    } catch {
                        print("Failed to refresh: \(error)")
                    }
                }
            } label: {
                Text("Refresh")
            }
                // use the query results in a view
            ForEach(queryRef.data?.movies ?? [], id: \.self.id) { movie in
                    Text(movie.title)
                }
            }
    }
    @MainActor
    func refresh() async throws {
        _ = try await queryRef.execute()
    }
}

Queries also support one-shot execution.

let resultData = try await DataConnect.moviesConnector.listMoviesByGenreQuery.execute(genre: "Sci-Fi")

Prototype and test your iOS application

Instrument clients to use a local emulator

You can use the Data Connect emulator, whether from the Data Connect VS Code extension or from the CLI.

Instrumenting the app to connect to the emulator is the same for both scenarios.

let connector = DataConnect.moviesConnector
// Connect to the emulator on "127.0.0.1:9399"
connector.useEmulator()

// (alternatively) if you're running your emulator on non-default port:
connector.useEmulator(port: 9999)

// Make calls from your app

Data types in Data Connect SDKs

The Data Connect server represents common and custom GraphQL data types. These are represented in the SDK as follows.

Data Connect Type Swift
String String
Int Int
Float Double
Boolean Bool
UUID UUID
Date FirebaseDataConnect.LocalDate
Timestamp FirebaseCore.Timestamp
Int64 Int64
Any FirebaseDataConnect.AnyValue