A Swift SDK for building extensions for Tophat.
📝 TophatKit will eventually be available as a standalone package, but is temporarily hosted directly inside the Tophat repo. If you want to start building your own extensions right away, clone the
tophat
repo and reference theTophatSDK
directory as a local Swift package.
TophatKit makes it easy to quickly build custom integrations for Tophat. Currently, TophatKit lets you build your own artifact providers so that you can use Swift to integrate Tophat with your CI or storage solution to retrieve artifacts.
A Tophat extension is a Generic Extension target in Xcode and is hosted in a standalone application, similar to Safari extensions. Some of Tophatʼs core features are defined in extensions as well.
This guide assumes that you are familiar with Xcode and basic macOS application structure.
Decide whether you want to host your extension directly in Tophat so that all Tophat users can have access, or whether you need a private extension that is exclusive to your organizationʼs systems. If you want to host in Tophat, open the Tophat Xcode project. If you want to create a private extension, create a new Xcode project and create a macOS application.
- Go to File → New → Target.
- Select Generic Extension.
- Choose a product name, and ensure that “Supports User Interface” is selected, even if you do not plan to support a settings interface.
- Once the target is created, add the TophatSDK Swift package to your project and ensure that it is linked to your new Generic Extension target.
- Open the
Info.plist
file for your extension and ensure that theEXExtensionPointIdentifier
is set tocom.shopify.Tophat.extension
.
Start by opening the MyExtension.swift
file at the root of your extension target (the name will match the name of the product you chose) and replace the contents of the file with the following:
// MyExtension.swift
import TophatKit
@main
struct MyExtension: TophatExtension {
static let title: LocalizedStringResource = "My Extension"
}
This is the default structure for an extension. The extension may then conform to one or more additional protocols to support additional functionality, such as ArtifactProviding
and SettingsProviding
.
To add an artifact provider, start by creating a new Swift file to host the artifact provider in. Weʼll use Google Cloud Storage as an example:
// 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.
}
}
Youʼll notice a few things here:
- The
id
is used to identify the artifact provider when using Tophat URLs ortophatctl
. - The
title
is what is displayed in various parts of the Tophat user interface. - There are two
Parameter
values. An artifact provider receives zero or more parameters as inputs when it is called. Use the@Parameter
property wrapper to define parameters for your artifact provider. - The
retrieve()
function is the implementation of the artifact provider. Use the parameters to make the necessary requests to retrieve the artifact. - The
cleanUp(localURL:)
function defines what needs to be done to delete the downloaded artifact produced byretrieve()
. You must implement this function.
Now that the artifact provider is created, register it in MyExtension.swift
:
// MyExtension.swift
import TophatKit
@main
struct MyExtension: TophatExtension, ArtifactProviding {
static let title: LocalizedStringResource = "My Extension"
static var artifactProviders: some ArtifactProviders {
GoogleCloudStorageArtifactProvider()
}
}
Build and run and you should now have a functioning extension and artifact provider!
If the user needs to be able to configure the extension (e.g. to provide an API key), conform MyExtension
to SettingsProviding
:
// GoogleCloudExtension.swift
import SwiftUI
import TophatKit
@main
struct MyExtension: TophatExtension, ArtifactProviding, SettingsProviding {
// ...
static var settings: some View {
Text("Settings")
}
}
The settings
property accepts a full SwiftUI view. The user can access the settings view by going to Tophat → Settings → Extensions, and clicking the info icon next to the extension.
See the contribution guidelines for more information.