Shopify Mobile Buy SDK for iOS

repository·main·Indexed 19 days ago

https://github.com/shopify/mobile-buy-sdk-ios

A Swift library that enables developers to build custom Shopify storefront experiences in iOS apps by connecting to the Storefront GraphQL API. It provides a network layer via Graph.Client for executing queries and mutations, supports opt-in caching, and includes tools for generating type-safe query builders and response classes using GraphQLSwiftGen.

Tokens
6.9K
Snippets
27
Records
28
Agent score
66%

What's inside mobile-buy-sdk-ios

  1. Configure caching for queries

    main

    Graph.Client provides an opt-in caching layer for query operations to reduce bandwidth and latency.

    Note: Caching is not available for mutation operations or any requests that provide a retryHandler.

    Cache Policies

    • .cacheOnly: Fetch from cache only. Returns error if not found.
    • .networkOnly: Fetch from network only. Ignores cache.
    • .cacheFirst(expireIn: Int): Check cache first. If missing or older than expireIn, fetch from network.
    • .networkFirst(expireIn: Int): Fetch from network first. If network fails and cache is not older than expireIn, return cached data.

    Usage

    You can enable caching client-wide by setting the cachePolicy property on the Graph.Client instance, or override it for a specific request using the cachePolicy parameter in queryGraphWith.

    // Client-wide caching
    let client = Graph.Client(shopDomain: "...", apiKey: "...")
    client.cachePolicy = .cacheFirst
    
    // Per-request override
    let task = client.queryGraphWith(query, cachePolicy: .networkFirst(expireIn: 20)) { query, error in
        // ...
    }
  2. Querying Nodes and using the Node protocol

    main

    The GraphQL schema defines a Node interface with an id field. The SDK allows you to query any object by its ID using the .node(id:) method. However, because the result is returned as a generic node, you must cast it to the specific generated type (e.g., Storefront.Product) to access its properties. Incorrect casting will result in a runtime exception.

    let id    = GraphQL.ID(rawValue: "gid://shopify/Product/123")
    let query = Storefront.buildQuery { $0
        .node(id: id) { $0
            .onProduct { $0
                .id()
                .title()
            }
        }
    }
    
    // response: Storefront.QueryRoot
    let product = response.node as! Storefront.Product
  3. Handle Graph.QueryError

    main

    The completion handler for requests returns an optional Graph.QueryError.

    Crucial: error and response are NOT mutually exclusive. A request can return both a non-nil error and a non-nil response. An error might represent a network issue (e.g., HTTP status code) or a GraphQL issue (e.g., invalid syntax).

    Common error types include:

    • .http(statusCode): Represents HTTP-level errors.
    • .invalidQuery: Returns an array of Reason objects containing detailed debugging information (not intended for end-users).
    let task = client.queryGraphWith(query) { response, error in
        if let response = response {
            // Do something
        } else {
            if let error = error, case .http(let statusCode) = error {
                print("Query failed. HTTP error code: \(statusCode)")
            }
        }
    }
    task.resume()
  4. Understand Mobile Buy SDK versioning

    main

    As of version 2025.1.0, the SDK uses a modified CalVer (Calendar Versioning) scheme: yyyy.mm.patch.

    • The first two components (yyyy.mm) match the Shopify Storefront GraphQL API version.
    • The third component (patch) corresponds to non-breaking bug fixes within an API version cycle.

    Warning: Unlike SemVer, there may be breaking GraphQL schema changes between "minor" versions because they are tied to API release cycles.

  5. Configure request retries with RetryHandler

    main

    You can enable retry or polling behavior for both queries and mutations by providing an optional RetryHandler.

    Create a handler with a condition. If both handler.condition and handler.canRetry evaluate to true, the Client will continue executing the request. By default, retryHandler is nil and no retry behavior is provided.

    let handler = Graph.RetryHandler<Storefront.QueryRoot>() { (query, error) -> Bool in
        if myCondition {
            return true // will retry
        }
        return false // will complete the request, either succeed or fail
    }
  6. Install the Mobile Buy SDK

    main

    The Mobile Buy SDK can be integrated into your iOS application using several dependency managers. Swift Package Manager is the recommended approach.

    ### Swift Package Manager
    Follow Apple's guide for [adding a package dependency to your app](https://developer.apple.com/documentation/xcode/adding_package_dependencies_to_your_app).
  7. Install the Mobile Buy SDK via Dynamic Framework

    main

    To install as a dynamic framework, follow these steps:

    1. Add Buy as a git submodule:
      git submodule add git@github.com:Shopify/mobile-buy-sdk-ios.git
    2. Update all submodules:
       ```bash
    git submodule update --init --recursive
    1. Drag Buy.xcodeproj into your application project.
    2. Add Buy.framework target as a dependency in Build Phases > Target Dependencies.
    3. Link Buy.framework in Build Phases > Link Binary With Libraries.
    4. Copy the framework into the bundle: Create a New Copy Files Phase, set Destination to Frameworks, and add Buy.framework.
    5. Import the module in your code using import Buy.
    git submodule add git@github.com:Shopify/mobile-buy-sdk-ios.git
    git submodule update --init --recursive
  8. Proceed to Checkout

    main

    To start the purchase process, retrieve the checkoutUrl from the Cart object.

    Recommended Approach: Use the Mobile Checkout SDK for a native experience.

    Alternative: Open the URL in SFSafariViewController or a standard web browser.

    // Recommended: Mobile Checkout SDK
    import UIKit
    import ShopifyCheckout
    
    class MyViewController: UIViewController {
        func proceedToCheckout() {
            let checkoutURL = // retrieve from your `Cart` object
    
            ShopifyCheckout.present(
                checkout: checkoutURL, from: self, delegate: self
            )
        }
    }
    
    // Alternative: SFSafariViewController
    import UIKit
    import SafariServices
    
    class MyViewController: UIViewController {
        func proceedToCheckout() {
            let checkoutURL = // retrieve from your `Cart` object
            present(SFSafariViewController(url: checkoutURL), animated: true)
        }
    }
  9. Generate Swift code from a GraphQL schema

    main

    To generate type-safe query builders and response classes, create a Ruby script that reads your GraphQL introspection JSON and initializes GraphQLSwiftGen.

    You can customize the generation by specifying a namespace via nest_under and defining custom_scalars to map GraphQL types to specific Swift types with custom serialization/deserialization logic.

    Example script:

    require 'graphql_swift_gen'
    require 'graphql_schema'
    require 'json'
    
    introspection_result = File.read("graphql_schema.json")
    schema = GraphQLSchema.new(JSON.parse(introspection_result))
    
    GraphQLSwiftGen.new(schema,
      nest_under: 'ExampleSchema',
      custom_scalars: [
        GraphQLSwiftGen::Scalar.new(
          type_name: 'Money',
          swift_type: 'NSDecimalNumber',
          deserialize_expr: ->(expr) { "NSDecimalNumber(string: #{expr}, locale: GraphQL.posixLocale)" },
          serialize_expr: ->(expr) { "#{expr}.description(withLocale: GraphQL.posixLocale)" },
        ),
      ]
    ).save("${Dir.pwd}/../MyApp/Source")
  10. Install GraphQLSwiftGen

    main

    GraphQLSwiftGen is a Ruby-based code generator that requires Ruby version 2.1 or later.

    It is recommended to include the repository as a git submodule:

    $ git submodule https://github.com/Shopify/graphql_swift_gen.git

    To manage dependencies, add the following to your application's Gemfile (assuming the submodule is in your project structure):

    gem 'graphql_swift_gen', path: 'graphql_swift_gen'

    Then run:

    $ bundle

    Important: The generated code depends on support/Sources/GraphQL.swift from the graphql_swift_gen repository. You must add this file to your Swift project along with the generated code.

  11. Initialize Graph.Client

    main

    Graph.Client is the network layer used to execute GraphQL query and mutation requests. It is built on top of URLSession and supports features like polling, retrying, and caching.

    To initialize a client, you need your shop's .myshopify.com domain and your API key. You can optionally provide a URLSession for custom network configuration or a Locale to support translated content if your store uses multiple languages.

    // Basic initialization
    let client = Graph.Client(
    	shopDomain: "shoes.myshopify.com",
    	apiKey:     "dGhpcyBpcyBhIHByaXZhdGUgYXBpIGtleQ"
    )
    
    // Initializing a client to return translated content
    let client = Graph.Client(
    	shopDomain: "shoes.myshopify.com",
    	apiKey:     "dGhpcyBpcyBhIHByaXZhdGUgYXBpIGtleQ",
            locale:     Locale.current
    )
  12. Install the Mobile Buy SDK via Carthage

    main
    1. Add the following line to your Cartfile:
      github "Shopify/mobile-buy-sdk-ios"
    2. Run carthage update.
    3. Follow the dynamic framework linking steps.
    4. Import the module using import Buy.
    github "Shopify/mobile-buy-sdk-ios"