Tophat Documentation

repository·main·Indexed 21 days ago

https://github.com/shopify/tophat

A mobile developer testing tool designed to simplify the installation and testing of iOS and Android applications by linking directly to CI artifacts. It features a companion CLI called tophatctl for managing Quick Launch apps and installations, as well as TophatKit, a Swift SDK for building custom artifact providers and extensions.

Tokens
4.3K
Snippets
23
Records
28
Agent score
77%

What's inside Tophat

  1. How Artifact Providers work in Tophat

    main

    Downloads in Tophat are powered by artifact providers.

    • Core Providers: Built-in to Tophat (e.g., http, shell).
    • Extensions: Custom providers that can be installed via the TophatKit SDK to integrate with custom build or caching systems.

    When triggering an installation, you must provide:

    1. The provider's ID.
    2. The provider's parameters.
    3. (Optional) The platform.
    4. (Optional) The destination.
    5. (Optional) A list of launch arguments.

    You can discover available provider IDs by running tophatctl list providers in your terminal.

  2. Create an Artifact Provider

    main

    Artifact providers allow Tophat to integrate with CI or storage solutions to retrieve files. To create one, implement the ArtifactProvider protocol.

    Key Requirements:

    • id: A unique identifier used by Tophat URLs and tophatctl.
    • title: The display name shown in the Tophat UI.
    • @Parameter: Use this property wrapper to define input parameters that the provider receives when called.
    • retrieve(): An async throws function that implements the logic to download the artifact. It must return a .result(localURL:).
    • cleanUp(localURL:): An async throws function that defines how to delete the downloaded artifact. This must be implemented.

    Registration:

    To make your provider available, conform your main extension to ArtifactProviding and implement the artifactProviders property.

    // GoogleCloudStorageArtifactProvider.swift
    
    import TophatKit
    
    struct GoogleCloudStorageArtifactProvider: ArtifactProvider {
      static let id = "gcs"
      static let title: LocalizedStringResource = "Google Cloud Storage"
    
      @Parameter(key: "bucket", title: "Bucket")
      var bucket: String
    
      @Parameter(key: "object", title: "Object")
      var object: String
    
      func retrieve() async throws -> some ArtifactProviderResult {
        let downloadedFileURL = // Your logic for downloading the build.
        return .result(localURL: downloadedFileURL)
      }
    
      func cleanUp(localURL: URL) async throws {
        // Perform clean up.
      }
    }
    
    // Registering in MyExtension.swift
    
    @main
    struct MyExtension: TophatExtension, ArtifactProviding {
      static let title: LocalizedStringResource = "My Extension"
    
      static var artifactProviders: some ArtifactProviders {
        GoogleCloudStorageArtifactProvider()
      }
    }
  3. System Requirements for Tophat

    main

    Before using Tophat, ensure your system meets the following requirements:

    • macOS: Version 15 or later.
    • iOS Development: Xcode 15 or newer. Physical devices must run iOS 17 or later. All simulator versions are supported.
    • Android Development: Android Studio and Android toolchains with a working adb and avdmanager environment.
  4. Integrate Tophat using URLs

    main

    Tophat supports tophat:// and http:// schemes for triggering installations. The tophat:// scheme is preferred as it does not navigate away from the current page. When creating links for websites, it is recommended to use "Install with Tophat" as the link text.

    URL Structure

    An installation request requires a provider ID and its specific parameters. You can optionally specify the platform, destination, and arguments.

    Format: tophat://install/<provider_id>?<parameters>

    Specifying Multiple Artifacts (Recipes)

    To support multiple destinations (e.g., both a simulator and a physical device) in a single link, repeat the query parameters for each configuration. Tophat will select the appropriate parameters based on the connected device.

    tophat://install/gcs?bucket=<bucket>&object=<object>&platform=ios&destination=device&bucket=<bucket>&object=<object>&platform=ios&destination=simulator
  5. Create a new Tophat extension target

    main

    A Tophat extension is implemented as a Generic Extension target in Xcode. Follow these steps to set up the target:

    1. In Xcode, go to File → New → Target.
    2. Select Generic Extension.
    3. Choose a product name and ensure “Supports User Interface” is selected (even if you don't plan to provide a settings interface).
    4. Add the TophatSDK Swift package to your project and link it to your new Generic Extension target.
    5. Open the extension's Info.plist and set the EXExtensionPointIdentifier to com.shopify.Tophat.extension.
  6. Install TophatKit for extension development

    main

    TophatKit is a Swift SDK for building Tophat extensions. Currently, it is hosted within the Tophat repository rather than as a standalone package.

    To use it, clone the tophat repository and reference the TophatSDK directory as a local Swift package in your Xcode project.

  7. Install Tophat

    main

    To install Tophat, download the latest signed universal binary from the GitHub releases page. Download the .zip file, extract it, and move the Tophat application to your Applications folder.

    Tophat will automatically check for updates. You will be prompted to enable automatic update checks on your second launch, or you can configure this manually in the Settings window.

    https://github.com/Shopify/tophat/releases/latest
  8. Use the Shell Script Provider via URL

    main

    The shell provider allows for custom download logic via shell scripts.

    Setup:

    1. Place your script in: ~/Library/Application Scripts/com.shopify.Tophat.TophatCoreExtension/.
    2. Ensure the script is executable: chmod +x <filename>.

    Execution: Invoke the script using the shell provider ID and the script parameter.

    Script Arguments: Your script will receive two positional arguments:

    • $1: The full path to a staging directory for temporary files/unzipping.
    • $2: The full path to the output directory. You must place exactly one artifact in this directory for Tophat to install it.
    tophat://install/shell?script=filename
  9. Use the Basic HTTP Provider via URL

    main

    The http provider is a built-in core provider that allows you to install an app directly from a public URL.

    Usage: tophat://install/http?url=<full_public_url>

    tophat://install/http?url=<full_public_url>
  10. Implement the basic extension structure

    main

    Every extension must conform to the TophatExtension protocol. The entry point is a struct marked with @main. You can extend this struct by conforming to other protocols like ArtifactProviding or SettingsProviding to add functionality.

    // MyExtension.swift
    
    import TophatKit
    
    @main
    struct MyExtension: TophatExtension {
      static let title: LocalizedStringResource = "My Extension"
    }
  11. Pass Launch Arguments to your App

    main

    You can pass command-line arguments to your application using the arguments query parameter in a Tophat URL. Arguments should be a comma-separated list.

    URL Example: tophat://install/gcs?bucket=<bucket>&object=<object>&arguments=one,two,three

    Retrieving Arguments in App:

    • iOS: Retrieve via ProcessInfo.
    • Android: Retrieve via intent argument extras using the TOPHAT_ARGUMENTS key (delivered as an array of strings) in the main activity.
    tophat://install/gcs?bucket=<bucket>&object=<object>&arguments=one,two,three
  12. Make an extension configurable with SettingsProviding

    main

    To allow users to configure your extension (e.g., providing an API key), conform your main extension struct to the SettingsProviding protocol. You must implement the settings property, which accepts a SwiftUI View.

    Users can access this view via Tophat → Settings → Extensions by clicking the info icon next to your extension.

    import SwiftUI
    import TophatKit
    
    @main
    struct MyExtension: TophatExtension, ArtifactProviding, SettingsProviding {
      // ...
    
      static var settings: some View {
        Text("Settings")
      }
    }