octokit.swift

nerdishbynatureMIT活跃维护

用 Swift 封装的 GitHub 与 GitHub Enterprise API 客户端,内置 OAuth 授权流程,方便 Swift 应用直接调用 GitHub 接口。

A Swift API Client for GitHub and GitHub Enterprise

0.14.0发布于 2024-12-07 08:01 UTC522star136fork同步于 2026-11-07 07:32 UTC

怎么上手

由 README 提炼,执行前请确认
dependencies: [.package(url: "https://github.com/nerdishbynature/octokit.swift", from: "0.11.0")]
硬件要求按 README 说明

README 未说明

下载:源码 zip·GitHub Releases(安装包)本站不镜像安装包

项目说明

由 README 自动翻译提炼

Swift 编写的 GitHub/GitHub Enterprise API 客户端,让 Swift 应用直接调用 GitHub 接口

用 Swift 封装的 GitHub 与 GitHub Enterprise API 客户端,内置 OAuth 授权流程,方便 Swift 应用直接调用 GitHub 接口。

核心能力

  • 用 TokenConfiguration 或 OAuthConfiguration 管理认证
  • 支持 OAuth 登录流程并处理回调 URL
  • 封装用户、仓库、Star、关注、Issue、PR、Release 等接口
  • 提供 *Paginated 变体解析 Link 头实现分页
  • 支持 async/await 逐页拉取全部数据

容易踩的坑

OAuth 流程返回的 accessToken 需自行存储,后续请求直接用 TokenConfiguration;GitHub Enterprise 需显式传入 api/v3 的 url 与 webURL;贡献代码需本地另装 SwiftFormat,它不是 SPM 依赖

内容由模型根据 README 生成,可能不完整或不准确,以官方文档为准。看原文 README

最近发版

5 个版本0.14.0
  1. 0.14.0最新正式版2024-12-07 · 2024-12-07 08:01 UTC

    What's Changed

    • Update Configuration.swift handleOpenURL and authorize to support async await
      @LucasCoderT #192
    • add some more fields to repo, and structure for organization
      @armcknight #191
    • Use Xcode 16 and swift 5.10
      @pietbrauer #196
    • feat: add repo topics
      @armcknight #197
    • Extend the Pull Request API
      @4np #195

    New Contributors

    Full Changelog: https://github.com/nerdishbynature/octokit.swift/compare/0.13.0...0.14.0

  2. 0.13.02024-08-02 · 2024-08-02 07:35 UTC
    查看更新说明

    What's Changed

    Full Changelog: https://github.com/nerdishbynature/octokit.swift/compare/0.12.0...0.13.0

  3. 0.12.02022-10-10 · 2022-10-10 08:34 UTC
    查看更新说明

    Changes

    • Add support for Requested Reviewers and Drafts #123 @DayBySay
    • Add parameter to filter pull requests by branch #126 @aaron-foreflight
    • Add support for notifications #127 @LucasCoderT
    • Add parameters to fetch releases by tag #141 @417-72KI
    • Add support for showing contents of a gists file #139 @HassanElDesouky
    • Add support for starring a repository #142 @417-72KI
    • Add optional generation of release notes #149 @integer256
    • Add support async/await #134 #150 @pietbrauer @417-72KI

    Tests

    • User docker for Linux Builds #124 #125 @pietbrauer
    • Add swiftformat #130 @pietbrauer

    Thanks to everyone for making this huge release possible by creating and reviewing PRs and Issues! ❤️

  4. 0.11.02021-02-14 · 2021-02-14 10:51 UTC
    查看更新说明

    Changes

    • Replace query param auth with authorization header auth
      #104 @joshdholtz
    • Fix capitalization in enum Openness
      #106 @MaxDesiatov
    • Add base and head branch refs to PullRequest model
      #109 @kolyasev
    • Add support for Commit Statuses
      #110 @kolyasev
    • Add missing properties of User
      #114 @mvolpato
    • Fetch issue comments
      #115 @mvolpato
    • Implement the API endpoint for listing releases
      #116 @417-72KI
    • Fix tests and decoding issues
      #121 @dato @pietbrauer

    Tests

    • Test with Linux Swift 5.2 on Travis
      #108 @MaxDesiatov
    • Fix swift test invocations on macOS
      #107 @MaxDesiatov
    • GitHub actions
      #120 @pietbrauer
    • Fix tests and decoding issues
      #121 @dato @pietbrauer

    Docs

    • Make examples in README.md consistent
      #105 @MaxDesiatov
    • Use label 'name' in sample calls to user() in README.md
      #118 @dato

    Thanks to everyone for making this huge release possible by creating and reviewing PRs and Issues! ❤️

  5. 0.10.1SwiftPM 5.22020-03-31 · 2020-03-31 09:45 UTC
    查看更新说明
    • SwiftPM Changes 5.2. #102 #103
      @bofeizhu @f-meloni @pietbrauer

用户点评

登录用户的真实使用记录,可按版本和硬件筛选
共 0 条

    还没有人点评。第一个说出真实体验的人最有价值。

    README 原文

    未翻译

    Octokit.swift

    Installation

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

    Authentication

    Octokit supports both, GitHub and GitHub Enterprise. Authentication is handled using Configurations.

    There are two types of Configurations, TokenConfiguration and OAuthConfiguration.

    TokenConfiguration

    TokenConfiguration is used if you are using Access Token based Authentication (e.g. the user offered you an access token he generated on the website) or if you got an Access Token through the OAuth Flow

    You can initialize a new config for github.com as follows:

    let config = TokenConfiguration("YOUR_PRIVATE_GITHUB_TOKEN_HERE")
    

    or for GitHub Enterprise

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

    After you got your token you can use it with Octokit

    Octokit(config).me() { response in
      switch response {
      case .success(let user):
        print(user.login as Any)
      case .failure(let error):
        print(error)
      }
    }
    

    OAuthConfiguration

    OAuthConfiguration is meant to be used, if you don't have an access token already and the user has to login to your application. This also handles the OAuth flow.

    You can authenticate an user for github.com as follows:

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

    or for GitHub Enterprise

    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"])
    

    After you got your config you can authenticate the user:

    // 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
    }
    
    func loadCurrentUser(config: TokenConfiguration) {
      Octokit(config).me() { response in
        switch response {
        case .success(let user):
          print(user.login)
        case .failure(let error):
          print(error)
        }
      }
    }
    

    Please note that you will be given a TokenConfiguration back from the OAuth flow. 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.

    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)")
      }
    }
    
    

    Users

    Get a single user

    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
      }
    }
    

    Get the authenticated user

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

    Repositories

    Get a single repository

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

    Get repositories of authenticated user

    Octokit().repositories() { response in
      switch response {
        case .success(let repository):
          // do something
        case .failure(let error):
          // handle any errors
      }
    }
    

    Starred Repositories

    Get starred repositories of some user

    let username = "username"
    Octokit().stars(username) { response in
      switch response {
        case .success(let repositories):
          // do something with the repositories
        case .failure(let error):
          // handle any errors
      }
    }
    

    Get starred repositories of authenticated user

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

    Follower and Following

    Get followers of some user

    let username = "username"
    Octokit().followers(username) { response in
      switch response {
        case .success(let users):
          // do something with the users
        case .failure(let error):
          // handle any errors
      }
    }
    

    Get followers of authenticated user

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

    Get following of some user

    let username = "username"
    Octokit().following(username) { response in
      switch response {
        case .success(let users):
          // do something with the users
        case .failure(let error):
          // handle any errors
      }
    }
    

    Get following of authenticated user

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

    Issues

    Get issues of authenticated user

    Get all issues across all the authenticated user's visible repositories including owned repositories, member repositories, and organization repositories.

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

    Get a single issue

    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
        }
    }
    

    Open a new issue

    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
        }
    }
    

    Edit an existing issue

    Octokit(config).patchIssue("owner", repository: "repo", number: 1347, title: "Found a bug", body: "I'm having a problem with this.", assignee: "octocat", state: .Closed) { response in
        switch response {
        case .success(let issue):
            // do something with the issue
        case .failure:
            // handle any errors
        }
    }
    

    Comment an issue

    Octokit().commentIssue(owner: "octocat", repository: "Hello-World", number: 1, body: "Testing a comment") { response in
        switch response {
        case .success(let comment):
            // do something with the comment
        case .failure:
            // handle any errors
        }
    }
    

    Edit an existing comment

    Octokit().patchIssueComment(owner: "octocat", repository: "Hello-World", number: 1, body: "Testing a comment") { response in
        switch response {
        case .success(let comment):
            // do something with the comment
        case .failure:
            // handle any errors
        }
    }
    

    Pull requests

    Get a single pull request

    Octokit().pullRequest(owner: "octocat", repository: "Hello-World", number: 1) { response in
        switch response {
            case .success(let pullRequests):
                // do something with a pull request
            case .failure:
                // handle any errors
         }
    }
    

    List pull requests

    Octokit().pullRequests(owner: "octocat", repository: "Hello-World", base: "develop", state: Openness.Open) { response in
        switch response {
            case .success(let pullRequests):
            // do something with a pull request list
            case .failure:
            // handle any errors
        }
    }
    

    Update an exisiting Pull Request

    Octokit().patchPullRequest(session, owner: "octocat", repository: "Hello-World", number: 1, title: "Updated title", body: "The updated body", state: .open, base: "base-branch", mantainerCanModify: true) { response in
        switch response {
            case .success(let pullrequest):
            // do something with the pullrequest
            case .failure:
            // handle any errors
        }
    }
    

    Releases

    Create a new release

    Octokit().postRelease(owner: "octocat", repository: "Hello-World", tagName: "v1.0.0", targetCommitish: "master", name: "v1.0.0 Release", body: "The changelog of this release", prerelease: false, draft: false) { response in
    	switch response {
            case .success(let release):
            // do something with the release
            case .failure:
            // handle any errors
        }
    }
    

    Pagination

    Several list endpoints have a *Paginated variant that returns a PaginatedResponse<T> containing both the decoded values and a PageInfo parsed from the GitHub Link response header.

    Fetch a single page with pagination info

    let config = TokenConfiguration(bearerToken: "your_token")
    Octokit(config).issuesPaginated(owner: "octocat", repository: "Hello-World") { response in
        switch response {
        case .success(let paginated):
            let issues = paginated.values          // [Issue]
            let hasMore = paginated.pageInfo.hasNextPage
            let nextURL = paginated.pageInfo.next  // URL? pointing to the next page
            // do something with issues
        case .failure:
            // handle any errors
        }
    }
    

    Fetch all pages (async)

    func allIssues(owner: String, repo: String) async throws -> [Issue] {
        let config = TokenConfiguration(bearerToken: "your_token")
        let octokit = Octokit(config)
        var all: [Issue] = []
        var page = "1"
    
        while true {
            let paginated = try await octokit.issuesPaginated(owner: owner, repository: repo, page: page)
            all += paginated.values
    
            guard paginated.pageInfo.hasNextPage,
                  let nextURL = paginated.pageInfo.next,
                  let components = URLComponents(url: nextURL, resolvingAgainstBaseURL: false),
                  let nextPage = components.queryItems?.first(where: { $0.name == "page" })?.value
            else { break }
    
            page = nextPage
        }
    
        return all
    }
    

    Paginated variants are available for: myIssuesPaginated, issuesPaginated, pullRequestsPaginated, repositoriesPaginated, myNotificationsPaginated, listRepositoryNotificationsPaginated, listReleasesPaginated, labelsPaginated.

    Formatting

    OctoKit uses SwiftFormat for code style. It is not declared as a Swift Package Manager dependency (to avoid propagating it to consumers), so install it locally before contributing:

    brew install swiftformat
    # or
    mint install nicklockwood/SwiftFormat
    

    Run the formatter from the repo root:

    swiftformat .
    

    CI enforces the same rules via swiftformat --lint ..