Open-source project
nerdishbynature/octokit.swift avatar
nerdishbynature/octokit.swift

Octokit.swift: a GitHub API client for Swift apps

A Swift API Client for GitHub and GitHub Enterprise

521 stars136 forksSwiftMIT

At a glance

What is it?
Octokit.swift is a Swift client for the GitHub and GitHub Enterprise APIs, with token and OAuth configurations, a callback-based API over the repo and issue endpoints, and a bundled command-line tool. It installs through Swift Package Manager with one dependency line.
Who is it for?
Adopt Octokit.swift if you are writing a Swift app or server tool that needs to read or write GitHub data without hand-building requests, since it covers users, repositories, stars, social graph and issues with typed results. Do not adopt it as a complete GitHub client, because the documented surface is a partial one and the newer REST endpoints are not covered.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 96 days ago.
What is it written in?
Mainly Swift, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What it is, and the shape of the API

Octokit.swift is described as a Swift API Client for GitHub and GitHub Enterprise, and it is worth being precise about what that means in practice, because the API surface is not the whole of GitHub's.

Every documented method takes a completion handler with a result enum that has success and failure cases, and the pattern is consistent throughout.

swift
let username = ... // set the username
Octokit().user(name: username) { response in
  switch response {
  case .success(let user):
    // do something with the user
  case .failure(let error):
    // handle any errors
  }
}

That is a callback-style API rather than async/await, and for a library that has been around since before Swift concurrency, that is the interesting detail. The primary language is Swift, the licence is MIT, the default branch is main, and the recent releases are 0.14.0 from 2024-12-07, 0.13.0 from 2024-08-02 and 0.12.0 from 2022-10-10. The last push was on 2026-07-19.

The repository layout shows two products in one. OctoKit/ holds the library itself, OctoKitCLI/ holds a command-line tool built on it, and Tests/ holds the test suite. There is a .swift-version file, a .swiftformat configuration for formatting, a Package.resolved, a scripts/ directory and a .devcontainer/ directory.

The topic tags list api-client, github, github-api, github-enterprise, oauth-flow, octokit and swift. The presence of github-enterprise as a first-class tag is the thing that distinguishes this from a plain GitHub client, and it shows up throughout the configuration API rather than being an afterthought.

The documented endpoints cover a coherent slice of the API. Users, via user(name:) and me(). Repositories, via repository(owner, name) and repositories(). Starred repositories, via stars(username) and myStars(). The follower and following graph, with followers, myFollowers, following and myFollowing. And issues, with myIssues(), issue(owner, repository:number) and postIssue for opening one and an edit method for an existing one.

Two configuration types, and why the choice matters

Authentication is handled using Configurations, and there are two types, TokenConfiguration and OAuthConfiguration. Octokit supports both GitHub and GitHub Enterprise.

TokenConfiguration is used if you are using access token based authentication, for example the user offered you an access token they generated on the website, or if you got an access token through the OAuth flow.

swift
let config = TokenConfiguration("YOUR_PRIVATE_GITHUB_TOKEN_HERE")

The same constructor takes a URL for GitHub Enterprise, pointing at the API v3 path of your instance.

swift
let config = TokenConfiguration("YOUR_PRIVATE_GITHUB_TOKEN_HERE", url: "https://github.example.com/api/v3/")

OAuthConfiguration is meant to be used if you do not have an access token already and the user has to log in to your application. It also handles the OAuth flow.

swift
let config = OAuthConfiguration(token: "<Your Client ID>", secret: "<Your Client secret>", scopes: ["repo", "read:org"])
let url = config.authenticate()

And the Enterprise variant takes the API URL, the web URL, the client id, the secret and the scopes.

swift
let config = OAuthConfiguration("https://github.example.com/api/v3/", webURL: "https://github.example.com/", token: "<Your Client ID>", secret: "<Your Client secret>", scopes: ["repo", "read:org"])

The OAuth path returns a TokenConfiguration rather than doing the work itself, and the README is explicit about the consequence. You will be given a TokenConfiguration back from the OAuth flow, and you have to store the accessToken yourself. If you want to make further requests it is not necessary to do the OAuth flow again; you can just use a TokenConfiguration.

swift
let token = // get your token from your keychain, user defaults (not recommended) etc.
let config = TokenConfiguration(token)
Octokit(config).user(name: "octocat") { response in
  switch response {
  case .success(let user):
  	print("User login: \(user.login!)")
  case .failure(let error):
  	print("Error: \(error)")
  }
}

That last block is where the practical advice lives: the token goes in your keychain, and user defaults are explicitly marked as not recommended. For an OAuth-driven app, that means the library hands you a credential and the storage decision is yours, which is the right division of responsibility but also means the library gives you no help with it.

The scopes are passed explicitly rather than defaulted, so a caller asking for repo and read:org is asking for the scope set the app actually needs.

Installing it and the OAuth callback wiring

Installation is Swift Package Manager, and it is one dependency line.

swift
import PackageDescription

let package = Package(
  name: "MyAwesomeApp",
    dependencies: [
      .package(url: "https://github.com/nerdishbynature/octokit.swift", from: "0.11.0"),
    ]
  )

The from constraint starts at 0.11.0, and the current release is 0.14.0, so the resolved version depends on your own dependency graph.

The part of the README worth reading closely is the OAuth callback handling, because it is where an iOS app goes wrong most often. The README shows the AppDelegate code.

swift
// AppDelegate.swift

config.authenticate()

func application(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject?) -> Bool {
  config.handleOpenURL(url) { config in
    self.loadCurrentUser(config) // purely optional of course
  }
  return false
}

Two things are notable. First, the delegate method calls config.handleOpenURL and then returns false, which is the correct behaviour when another handler owns the callback, and the returned configuration is passed to a completion handler rather than returned directly. Second, the loadCurrentUser call is commented as purely optional, which tells you the flow works without immediately fetching the user.

The method signature itself, with sourceApplication and annotation, is the older UIApplicationDelegate shape rather than the modern scene-based variant, so a current iOS project will need to adapt it. That is a small sign of the age of the API surface.

For a quick check that the package is wired correctly, the simplest thing to do is fetch the authenticated user.

swift
Octokit().me() { response in
  switch response {
    case .success(let user):
      // do something with the user
    case .failure(let error):
      // handle any errors
  }
}

The CLI in OctoKitCLI/ is a second way to exercise the same library from a terminal, which is useful for scripting GitHub interactions in a Swift-based toolchain without writing an app.

The issue endpoints, where the write path starts

The issue API is where the library stops being read-only, and the documentation there is the most practically useful part of the README for anyone building a tool.

Getting all issues across the authenticated user's visible repositories, including owned repositories, member repositories and organization repositories, is myIssues().

swift
Octokit(config).myIssues() { response in
    switch response {
        case .success(let issues):
        // do something with the issues
    case .failure:
        // handle any errors
    }   
}

A single issue is fetched by owner, repository name and number.

swift
let (owner, repo, number) = ("owner", "repo", 1347) // replace with actual owner, repo name, and issue number
Octokit(config).issue(owner, repository: repo, number: number) { response in
    switch response {
    case .success(let issue):
        // do something with the issue
    case .failure:
        // handle any errors
    }
}

Opening a new issue takes the owner, repository, title, body, an optional assignee and optional labels.

swift
Octokit(config).postIssue("owner", repository: "repo", title: "Found a bug", body: "I'm having a problem with this.", assignee: "octocat", labels: ["bug", "duplicate"]) { response in
    switch response {
    case .success(let issue):
        // do something with the issue
    case .failure:
        // handle any errors
    }
}

The labels array is the kind of detail that shows the API was written against the real REST surface rather than a simplified idea of it, and the edit method for an existing issue follows the same shape.

The whole callback pattern repeats across every endpoint, which is a virtue in one sense and a limitation in another. The consistency means there is one thing to learn, and the ergonomics of Swift's structured concurrency are not used at all. A modern codebase that has already adopted async/await will find itself wrapping every call in a continuation.

Where Octokit.swift is the wrong choice

There are three limits, and the first is about how much of the API is actually covered.

The first is scope. The documented surface is users, repositories, stars, the follower and following graph, and issues. GitHub's REST API is far larger than that: pull requests, workflows, releases, gists, teams, projects, notifications, reactions and the GraphQL API are all absent from the README. The library is a usable client for a specific set of operations, and for anything outside it you would be writing raw requests anyway.

The second is the release cadence, and it is genuinely a concern for adoption. The published versions stop at 0.14.0 from December 2024, with 0.13.0 in August 2024 and 0.12.0 in October 2022. The last push was on 2026-07-19, so commits are still landing, but the tagged releases stopped a long time before that. For a library that talks to a third-party API, that gap is the thing to think about: GitHub's API changes, and the release that adapts to a change is the one that matters, not the newest commit.

The callback API compounds it. There is no async/await variant in the documentation, and the delegate method signature is the older UIApplicationDelegate shape with sourceApplication and annotation, which a current iOS project using scenes has to rework. Both suggest a library written for an earlier Swift and an earlier iOS, and the work of modernising it falls on whoever adopts it.

The third is the GitHub Enterprise path. Supporting Enterprise is a genuine advantage, since it is where a lot of organisations actually run, and the configuration API handles it properly by taking both an API URL and a web URL. But it also means the library must track GitHub Enterprise's own versioning, which does not always match github.com, and there is no way to tell from the README how closely the two are kept in step.

The honest alternative depends on what you need. For a one-off script that needs to read a repository's issues, a few lines of URLSession against the REST API are less code than adding a package dependency. For an app that needs the OAuth flow with a token store you control, the same applies, and the flow is a browser redirect plus a token exchange. For a Swift project that wants a typed client and already depends on a broader GitHub toolkit, there are community Swift packages that cover more endpoints and do expose async/await.

What Octokit.swift offers is a small, MIT-licensed, typed client with Enterprise support in the configuration and a CLI alongside it. For a Swift developer who wants the last few GitHub calls typed rather than raw, that is a reasonable package. For a serious integration, the release gap and the callback-only API are the two things to resolve first.

Editorial conclusion

Adopt Octokit.swift if you are writing a Swift app or server tool that needs to read or write GitHub data without hand-building requests, since it covers users, repositories, stars, social graph and issues with typed results. Do not adopt it as a complete GitHub client, because the documented surface is a partial one and the newer REST endpoints are not covered. Before you start, note that the release line stopped at 0.14.0 in December 2024 even though the last push was on 2026-07-19, so pin a version and check it against your Swift toolchain.

Frequently asked questions

What is Octokit.swift?

It is a Swift API client for GitHub and GitHub Enterprise, covering users, repositories, starred repositories, the follower and following graph, and issues. It installs through Swift Package Manager and ships both a library in OctoKit/ and a command-line tool in OctoKitCLI/.

How do I authenticate with Octokit.swift?

Using one of two configuration types. TokenConfiguration takes an access token, with an optional URL for GitHub Enterprise, and is used for token-based auth or a token obtained through the OAuth flow. OAuthConfiguration takes a client id, secret and scopes, and its authenticate() method runs the OAuth flow and returns a TokenConfiguration you then use for requests.

How do I install Octokit.swift in a Swift project?

Through Swift Package Manager, by adding .package(url: "https://github.com/nerdishbynature/octokit.swift", from: "0.11.0") to the dependencies array of your Package.swift file. The current published release is 0.14.0.

Where should I store the access token after the Octokit.swift OAuth flow?

You store it yourself, since the OAuth flow hands you back a TokenConfiguration whose accessToken you must keep. The README suggests getting it from your keychain and marks user defaults as not recommended, and notes that further requests do not require repeating the OAuth flow.

Which GitHub API endpoints does Octokit.swift cover?

The documented set covers users through user(name:) and me(), repositories through repository(owner, name) and repositories(), starred repositories through stars(username) and myStars(), the social graph through followers, myFollowers, following and myFollowing, and issues through myIssues(), issue(owner, repository:number), postIssue and an edit method for an existing issue.

Official sources

  1. Issues
  2. License: MIT
  3. nerdishbynature/octokit.swift on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/nerdishbynature-octokit-swift.svg)](https://hysenlabs.com/projects/nerdishbynature-octokit-swift)

Was this useful?