IOS App Modularization With Apollo And SPM

In the modern development landscape, GraphQL has revolutionized the way we think about APIs and data retrieval. When it comes to iOS development, integrating GraphQL can be a breeze, thanks to libraries like Apollo. But how do you take it a step further and make your implementation modular using Swift Package Manager (SPM)? This article will guide you through the benefits, drawbacks, and step-by-step process of achieving this modular approach.

Why Apollo?

Apollo has become one of the most popular GraphQL clients owing to its robust set of features, including intelligent caching, real-time updates, and a strong type system. But one of its less talked-about advantages is its compatibility with Swift and native support for modularization through SPM.

The Need for Modularization

As applications grow, maintaining clean, reusable code becomes challenging. Modularization, or the process of dividing a program into separate sub-programs, is a strategy to manage this complexity. By creating a separate module for the Apollo GraphQL client, we can:

How To Implement a Modular Apollo GraphQL Client

Step 1: Initialize a New Swift Package

In your project directory, run:

 
swift package init --type library GraphQLClient


This command initializes a new Swift package named GraphQLClient.

Step 2: Configuring the Swift Package

Now, let’s create a robust Package.swift that defines our module along with a separate testing target and a plugin for the Apollo CLI.

 
// swift-tools-version: 5.7
import PackageDescription

let package = Package(
    name: "GraphQLClient",
    platforms: [
        .iOS(.v14),
        .macOS(.v10_14)
    ],
    products: [
        .library(
            name: "GraphQLClient",
            targets: ["GraphQLClient"]),
        .library(name: "GraphQLClientTesting",
                 targets: ["GraphQLClientTesting"]),
        .plugin(name: "GenerateApolloCli",
                targets: ["GenerateApolloCli"])
    ],
    dependencies: [
        .package(url: "https://github.com/apollographql/apollo-ios.git",
                 from: "1.3.3")
    ],
    targets: [
        .target(
            name: "GraphQLClient",
            dependencies: [.product(name: "Apollo", package: "apollo-ios")],
            path: "./Sources",
            exclude: ["Tests"],
            swiftSettings: [
                .unsafeFlags(["-suppress-warnings"])
            ]),
        .target(name: "GraphQLClientTesting",
                dependencies: [.product(name: "ApolloTestSupport", package: "apollo-ios")],
                path: "./Sources/Tests"),
        .plugin(
            name: "GenerateApolloCli",
            capability: .command(
                intent: .custom(
                    verb: "apollo-cli-generate", // Verb used from the command line
                    description: "Generates graphql"),
                permissions: [
                    .writeToPackageDirectory(reason: "Generate code for graphql")
                ]),
            dependencies: [
                .product(name: "apollo-ios-cli", package: "apollo-ios")
            ],
            path: "Plugins/GenerateApolloCli"
        )
    ]
)


Within this configuration, we’re defining three significant components:

1. GraphQL Client: GraphQLClient

This serves as our Apollo client, the core engine through which we’ll send GraphQL queries and mutations. By modularizing it, we ensure a clean separation from our application logic, allowing for easy updates and potential reuse across projects.

This target includes dependencies for Apollo and sets the path and exclusions for our source files, ensuring clean navigation and minimal build warnings.

2. Testing Module: GraphQLClientTesting

Aiming for solid testing practices, we separate our testing concerns by establishing a dedicated testing module.

This enables the usage of mock responses, creating a controlled environment for our integration tests and ensuring our app’s logic handles data correctly without making actual API calls.

3. Code Generation Plugin: GenerateApolloCli

Code generation is a pivotal feature in GraphQL development, automating the creation of query structures and types. With Apollo CLI’s code generation and our custom Xcode plugin, we enhance our development workflow.

This plugin allows developers to execute Apollo CLI code generation directly from Xcode, simplifying the process and enhancing productivity by reducing context-switching between the terminal and IDE.

Step 3: Apollo Codegen Configuration

When working with Apollo in Swift, the apollo-codegen-config.json file plays a pivotal role in steering code generation. It configures the Apollo CLI’s operations when it’s generating types and operations for your GraphQL queries. Let’s dissect a sample configuration:

 
{
    "schemaNamespace" : "MyNamespaceGraphql",
    "input" : {
        "operationSearchPaths" : ["**/*.graphql"],
        "schemaSearchPaths" : ["**/*.graphqls"]
    },
    "output" : {
        "testMocks" : {
            "absolute" : {
                "path": "./Sources/Tests/Mocks",
                "accessModifier": "public"
            }
        },
        "schemaTypes" : {
            "path" : "./Sources/GraphQLClient/Generated",
            "moduleType" : {
                "embeddedInTarget": {
                    "name": "GraphQLClient",
                    "accessModifier": "public"
                }
            }
        },
        "operations" : {
            "inSchemaModule" : {}
        }
    }
}


1. Schema Namespace

 
"schemaNamespace" : "MyNamespaceGraphql"


This defines the namespace for the generated schema types, ensuring that your GraphQL types are encapsulated under a dedicated namespace, MyNamespaceGraphql, preventing naming conflicts and ensuring clean integration within your Swift code.

2. Input Configuration

 
"input" : {
    "operationSearchPaths" : ["**/*.graphql"],
    "schemaSearchPaths" : ["**/*.graphqls"]
}


The input section dictates where Apollo should search for .graphql and .graphqls files within your project, allowing you to organize your GraphQL documents flexibly without restricting them to a single directory.

3. Output Configuration

The output section is more granular, controlling the destinations and access levels of generated code and mocks.

Step 4: Incorporating the Schema Definition Language (SDL) for Code Generation

The essence of interacting with a GraphQL API pivots on understanding the API’s schema—its types, queries, mutations, and subscriptions. The Schema Definition Language (SDL) is foundational in this, providing a structural and type definition of the API that Apollo utilizes to generate corresponding Swift code.

Why Is SDL Crucial?

The SDL provides a blueprint of the GraphQL API, describing all possible queries, mutations, and data structures in your API. Without it, Apollo’s codegen tool would lack the necessary context for generating types and operations that align with the API.

Embedding SDL in Your Project

To involve SDL in code generation, ensure the .graphqls file containing the SDL of your GraphQL API is placed in the path specified in your apollo-codegen-config.json.

Step 5: Defining Queries and Mutations With .graphql Files

Crafting and managing your queries and mutations is a quintessential step in shaping your GraphQL interactions and, consequently, the generated code via Apollo. Leveraging .graphql files allows you to articulate the exact operations your app will perform, ensuring Apollo generates only the requisite code.

Formulating .graphql Files

1. Define Precisely
Each .graphql file should encapsulate a single query, mutation, or subscription. This ensures clarity and makes tracking changes in version control systems like git more straightforward.

2. Organize Strategically
Store .graphql files in a logical, hierarchical directory structure that reflects their usage within your app. For instance, grouping all user-related operations within a /user directory.

 
# Example Query in a .graphql File
query GetUser($userID: ID!) {
    user(id: $userID) {
        id
        name
        email
    }
}


Tailoring Code Generation

By specifying the exact operations your app will utilize, Apollo CLI will generate Swift code that is:

Ensure that your .graphql files are stored in the directory specified in your apollo-codegen-config.json, enabling Apollo CLI to locate and utilize them during code generation.

 
"input" : {
    "operationSearchPaths" : ["**/*.graphql"]
}


With your queries and mutations strategically defined and organized, you not only streamline your code generation but also enhance the clarity and maintainability of your operations. The lean, tailored code generated by Apollo ensures your app remains optimized and robust, regardless of the complexity of your GraphQL API.

Your steps, from SDL incorporation to query and mutation definition, provide a seamless and efficient approach to leveraging GraphQL with Apollo in Swift, ensuring your development is not just robust and type-safe but also a pleasurable, coherent experience.

Advantages of Modularization

Drawbacks of Modularization

Conclusion

Implementing a modular Apollo GraphQL client via Swift Package Manager not only makes your codebase cleaner but also enhances reusability and maintainability. While there may be some initial setup overhead and additional complexities in dependency management, the long-term benefits often outweigh these drawbacks. By leveraging both Apollo and SPM, you can create robust, modular, and efficient iOS applications.

 

 

 

 

Top