A Travis v3 API client written in Swift

What's New

SwiftNIO support & dynamicMemberLookup

  • Add support for SwiftNIO EventLoopFuture based API
  • Separate out TravisV3Core package for Codable types.
  • Use keypath dynamicMemberLookup on Metadata & Embed
  • Add small example CLI using ArgumentParser
  • Currently depends on a fork of async-http-client until an escaping issue is resolved - PR here
  • Rename Meta -> Metadata


A Swift interface to the travis-ci v3 API. Supports travis-ci.org, travis-ci.com & on premise deployments.

Build Status | Read the docs


  • Install with SPM .package(url: "https://github.com/iainsmith/swift-travis", from: "0.3.0"),
  • The swift-travis package has the following 3 libraries you can use in your application.
Use-case interface target
iOS & Mac apps URLSession .product(name: "TravisClient", package: "swift-travis")
CLI & Server APPs EventLoopFuture .product(name: "TravisClientNIO", package: "swift-travis")
Build your own package Codable structs .product(name: "TravisV3Core", package: "swift-travis")

Quick example

import TravisClient

let key: String = ProcessInfo().environment["TRAVIS_TOKEN]"!
let client = TravisClient(token: key, host: .org)

client.userBuilds(query: query) { result in
  switch result {
    case let .success(builds):
    case let .failure(error):
      // error handling

Swift NIO examples

import TravisClientNIO

let key: String = ProcessInfo().environment["TRAVIS_TOKEN]"!
let client = TravisClient(token: key, host: .org) // You can also pass an `EventLoopGroup`

let builds = try client.builds(forRepository: repo).wait()

Travis API Concepts.

The api mirrors the names & concepts from the official Travis API documentation.

Minimal vs Standard Representation.

Each model object has two representations. A standard representation that includes all the attributes and a minimal representation that includes some attributes.

public struct MinimalJob: Codable, Minimal {
  public typealias Full = Job
  public let id: Int

public struct Job: Codable {
  public let id: Int
  public let number: String
  public let state: String
  // 10 other properties

Minimal vs Standard Job Example Travis documentation

If you need more information you can load a standard representation using the client.follow(embed:completion:) method

let build: Meta<Build>
let minimalJob: Embed<MinimalJob> = build.jobs.first! // don't do this in production code

client.follow(embed: minimalJob) { fullJob in
Modelling the hypermedia API

The Travis v3 API uses a custom hypermedia API spec, that is described on their website. The TravisV3Core targets has a generic Metadata<Object> struct.

public struct Metadata<Object: Codable>: Codable {
  public let type: String
  public let path: String
  public let pagination: Pagination<Object>?
  public let object: Object

let builds: Metadata<[Build]>
// dynamicMemberLookup means we can often use Metadata<[Build]> as [Build]
builds.count == builds.object.count

Metadata gives us direct access to the Pagination data and the underlying Object through dynamicMemberLookup.

The travis API often nests resources which is modelled with the Embed<Object> struct.

public struct Embed<Object: Codable>: Codable {
  public let type: String
  public let path: String?
  public let object: Object

struct Build {
  public let repository: Embed<MinimalRepository>
  public let branch: Embed<MinimalBranch>
  public let commit: Embed<MinimalCommit>

let build: Metadata<Build>
let branchName: String = build.branch.name
  • You can call client.follow(page:) to load the a page of results from the paginated API
  • Similarly you can all client.follow(embed:) to fetch the Full version of a MinimalResource. e.g MinimalBranch -> Branch.
import TravisClient

client.activeBuilds { (result: Result<MetaData<[Build]>, TravisError>) in

    /// You can also switch over the result
    switch result {
    case success(let builds: MetaData<[Build]>)
        // Find the number of active builds

        // Find the jobs associated with this build
        guard let job: Embed<MinimalJob> = jobs.first else { return }

        // Each API call returns one resource that has a 'standard representation' full object in this case supports hyper media so you can easily load the full object in a second request.
        client.follow(embed: job) { (jobResult: Result<MetaData<Job>>) in

        // Or follow a paginated request
        client.follow(page: builds.pagination.next) { nextPage in

    case error(let error):
        // handle error

Running the tests

# JSON parsing tests
> swift test

# Hit the travis.org API

The Integration tests only run if you have a TRAVIS_TOKEN environment variable set. This uses XCTSkipIf which requires Xcode 11.

Supported swift versions

If you are using Swift 5.1 or newer you can use the latest release If you need support for Swift 4.2 or older use version 0.2.0


  • Support paginated requests
  • Add User Model
  • Add Simple Query parameters
  • Add Stages Model
  • Add more typed sort parameters
  • Support Type safe Eager Loading.


  • Swift Tools 5.1.0
View More Packages from this Author


Last updated: Tue Sep 05 2023 11:26:06 GMT-0900 (Hawaii-Aleutian Daylight Time)