OAuth 流程返回的 accessToken 需自行存储,后续请求直接用 TokenConfiguration;GitHub Enterprise 需显式传入 api/v3 的 url 与 webURL;贡献代码需本地另装 SwiftFormat,它不是 SPM 依赖
octokit.swift
nerdishbynatureMIT活跃维护用 Swift 封装的 GitHub 与 GitHub Enterprise API 客户端,内置 OAuth 授权流程,方便 Swift 应用直接调用 GitHub 接口。
A Swift API Client for GitHub and GitHub Enterprise
dependencies: [.package(url: "https://github.com/nerdishbynature/octokit.swift", from: "0.11.0")]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 逐页拉取全部数据
容易踩的坑
- 0.14.0最新正式版2024-12-07 · 2024-12-07 08:01 UTC
What's Changed
- Update Configuration.swift
handleOpenURLandauthorizeto 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
- @armcknight made their first contribution in https://github.com/nerdishbynature/octokit.swift/pull/191
- @4np made their first contribution in https://github.com/nerdishbynature/octokit.swift/pull/195
Full Changelog: https://github.com/nerdishbynature/octokit.swift/compare/0.13.0...0.14.0
- Update Configuration.swift
- 0.13.02024-08-02 · 2024-08-02 07:35 UTC
查看更新说明
What's Changed
- async/await functions for Star/unstar
@417-72KI https://github.com/nerdishbynature/octokit.swift/pull/151 - Allow for authorization header to be set
@clayellis https://github.com/nerdishbynature/octokit.swift/pull/154 - Add patch pull request
@tboogh https://github.com/nerdishbynature/octokit.swift/pull/156 - Add devcontainer config
@pietbrauer https://github.com/nerdishbynature/octokit.swift/pull/157 - Sort keys in JSON Fixtures
@pietbrauer https://github.com/nerdishbynature/octokit.swift/pull/158 - Add Latest Release Endpoint
@brianmichel https://github.com/nerdishbynature/octokit.swift/pull/164 - Fix LableRouter Paths
@cwagdev https://github.com/nerdishbynature/octokit.swift/pull/165 - New endpoint:
Create a review for a pull request
@417-72KI https://github.com/nerdishbynature/octokit.swift/pull/169 - Add Swiftformat SPM Plugin
@pietbrauer https://github.com/nerdishbynature/octokit.swift/pull/170 - OctoKitCLI
@pietbrauer https://github.com/nerdishbynature/octokit.swift/pull/159 - Adding ability to create new pull request.
@ps2 https://github.com/nerdishbynature/octokit.swift/pull/167 - Move URLSession to Octokit instead of having it in every method
@pietbrauer https://github.com/nerdishbynature/octokit.swift/pull/160 - Add init for SwiftUI Preview
@zunda-pixel https://github.com/nerdishbynature/octokit.swift/pull/172 - Add
List pull requests filesendpoint
@417-72KI https://github.com/nerdishbynature/octokit.swift/pull/173 - fix: add missing documentation for
generateNotesparam ofpostRelease
@b-onc https://github.com/nerdishbynature/octokit.swift/pull/174 - Update SwiftFormat Plugin
@pietbrauer https://github.com/nerdishbynature/octokit.swift/pull/177 - Add pagination to PullRequests
@sfaxon https://github.com/nerdishbynature/octokit.swift/pull/176 - Run CI on main
@pietbrauer https://github.com/nerdishbynature/octokit.swift/pull/178 - Add pagination for Statuses
@sfaxon https://github.com/nerdishbynature/octokit.swift/pull/179 - add missing ci_activity to notification reasons
@amcleodca https://github.com/nerdishbynature/octokit.swift/pull/180 - Add search code endpoint
@chidiwilliams https://github.com/nerdishbynature/octokit.swift/pull/182 - dateDecodingStrategy added as described in Issue #184
@gregorFeigel https://github.com/nerdishbynature/octokit.swift/pull/185 - Update Xcode to 15.4
@pietbrauer https://github.com/nerdishbynature/octokit.swift/pull/187 - Milestone api
@leszko11 https://github.com/nerdishbynature/octokit.swift/pull/186 - Fix and add some endpoints for
review
@417-72KI https://github.com/nerdishbynature/octokit.swift/pull/171 - Implement Repository contents API
@Nikoloutsos https://github.com/nerdishbynature/octokit.swift/pull/189 - Async notifications support + added endpoints
@LucasCoderT https://github.com/nerdishbynature/octokit.swift/pull/183
Full Changelog: https://github.com/nerdishbynature/octokit.swift/compare/0.12.0...0.13.0
- async/await functions for Star/unstar
- 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! ❤️
- 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 testinvocations 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! ❤️
- Replace query param auth with authorization header auth
- 0.10.1SwiftPM 5.22020-03-31 · 2020-03-31 09:45 UTC
查看更新说明
- SwiftPM Changes 5.2. #102 #103
@bofeizhu @f-meloni @pietbrauer
- SwiftPM Changes 5.2. #102 #103
用户点评
登录用户的真实使用记录,可按版本和硬件筛选还没有人点评。第一个说出真实体验的人最有价值。
Octokit.swift
Installation
- Using Swift Package Manager:
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 ..