Type-safe Swift definitions for GitHub's REST API with dependency injection support.
swift-github-types provides comprehensive type definitions for GitHub's REST API, including:
- Traffic Analytics: Repository views, clones, paths, and referrers
- Stargazers: Star tracking with timestamps
- Repositories: Full repository management (create, read, update, delete, list)
- Collaborators: Repository collaboration and permissions
- OAuth: GitHub OAuth flow types
- Type Safety: Strongly-typed models with Codable conformance
- Dependency Injection: Using swift-dependencies for testability
Add to your Package.swift:
dependencies: [
.package(url: "https://github.com/coenttb/swift-github-types", from: "0.1.0")
]import GitHub_Types
import GitHub_Traffic_Types
import GitHub_Repositories_Types
import GitHub_Stargazers_Types// Traffic analytics response
let viewsResponse: GitHub.Traffic.Views.Response
print("Total views: \(viewsResponse.count)")
print("Unique visitors: \(viewsResponse.uniques)")
// Repository model
let repository: GitHub.Repository
print("Repository: \(repository.fullName)")
print("Stars: \(repository.stargazersCount)")
// Stargazers list response
let stargazers: GitHub.Stargazers.List.Response // Array of Stargazer
for stargazer in stargazers {
print("\(stargazer.user.login) starred at \(stargazer.starredAt)")
}import GitHub_Repositories_Types
// Create list request with filtering
let listRequest = GitHub.Repositories.List.Request(
visibility: .public,
type: .owner,
sort: .updated,
direction: .desc,
perPage: 30,
page: 1
)
// Use with a client implementation
let repositories = try await repositoriesClient.list(listRequest)
// Returns: [GitHub.Repository]let createRequest = GitHub.Repositories.Create.Request(
name: "my-new-repo",
description: "A Swift package for awesome things",
private: false,
hasIssues: true,
hasWiki: true,
autoInit: true,
gitignoreTemplate: "Swift",
licenseTemplate: "apache-2.0"
)
let repository = try await repositoriesClient.create(createRequest)
// Returns: GitHub.Repositorylet repository = try await repositoriesClient.get(
owner: "coenttb",
repo: "swift-github-types"
)
// Returns: GitHub.Repositorylet updateRequest = GitHub.Repositories.Update.Request(
description: "Updated description",
private: true,
hasIssues: true,
defaultBranch: "main"
)
let updated = try await repositoriesClient.update(
owner: "coenttb",
repo: "my-repo",
request: updateRequest
)
// Returns: GitHub.Repositorylet response = try await repositoriesClient.delete(
owner: "coenttb",
repo: "old-repo"
)
// Returns: GitHub.Repositories.Delete.Responseimport GitHub_Traffic_Types
let viewsResponse = try await trafficClient.views(
owner: "coenttb",
repo: "swift-github-types",
per: .day
)
print("Total views: \(viewsResponse.count)")
print("Unique visitors: \(viewsResponse.uniques)")
for view in viewsResponse.views {
print("Date: \(view.timestamp), Views: \(view.count)")
}let clonesResponse = try await trafficClient.clones(
owner: "coenttb",
repo: "swift-github-types",
per: .week
)
print("Total clones: \(clonesResponse.count)")
print("Unique cloners: \(clonesResponse.uniques)")let pathsResponse = try await trafficClient.paths(
owner: "coenttb",
repo: "swift-github-types"
)
for path in pathsResponse.paths {
print("\(path.path): \(path.count) views")
}let referrersResponse = try await trafficClient.referrers(
owner: "coenttb",
repo: "swift-github-types"
)
for referrer in referrersResponse.referrers {
print("\(referrer.referrer): \(referrer.count) views")
}import GitHub_Stargazers_Types
let request = GitHub.Stargazers.List.Request(
perPage: 100,
page: 1
)
let stargazers = try await stargazersClient.list(
owner: "coenttb",
repo: "swift-github-types",
request: request
)
for stargazer in stargazers {
print("\(stargazer.user.login) starred on \(stargazer.starredAt)")
}The package is organized into modular components:
- GitHub_Types_Shared: Common types (Repository, Owner, License, User, Timestamp)
- GitHub_Traffic_Types: Traffic analytics types and client protocol
- GitHub_Repositories_Types: Repository management types and client protocol
- GitHub_Stargazers_Types: Stargazer types and client protocol
- GitHub_Collaborators_Types: Collaboration types and client protocol
- GitHub_OAuth_Types: OAuth flow types and client protocol
- GitHub_Types: Umbrella client combining all feature modules
Each module follows a consistent pattern:
// GitHub namespace contains all feature modules
extension GitHub {
public enum Repositories: Sendable {} // Repository feature namespace
public enum Traffic: Sendable {} // Traffic feature namespace
}
// Request/Response types are nested under operation namespaces
extension GitHub.Repositories {
public enum List {}
public enum Create {}
public enum Update {}
}
extension GitHub.Repositories.List {
public struct Request: Codable, Equatable, Sendable { /* ... */ }
public typealias Response = [GitHub.Repository]
}// Feature-specific clients
extension GitHub.Repositories {
@DependencyClient
public struct Client: Sendable {
public var list: @Sendable (_ request: List.Request?) async throws -> List.Response
public var get: @Sendable (_ owner: String, _ repo: String) async throws -> GitHub.Repository
public var create: @Sendable (_ request: Create.Request) async throws -> GitHub.Repository
public var update: @Sendable (_ owner: String, _ repo: String, _ request: Update.Request) async throws -> GitHub.Repository
public var delete: @Sendable (_ owner: String, _ repo: String) async throws -> Delete.Response
}
}
// Main GitHub client combining all features
extension GitHub {
@DependencyClient
public struct Client: Sendable {
public var traffic: Traffic.Client
public var repositories: Repositories.Client
public var stargazers: Stargazers.Client
public var oauth: OAuth.Client
public var collaborators: Collaborators.Client
}
}Create mock clients for testing using swift-dependencies:
import GitHub_Types
import GitHub_Repositories_Types
import Dependencies
import Testing
@Test func testRepositoryListing() async throws {
// Create mock repository data
let mockRepo = GitHub.Repository(
id: .init(rawValue: 123),
nodeId: "MDEwOlJlcG9zaXRvcnkxMjM=",
name: "test-repo",
fullName: "user/test-repo",
private: false,
owner: GitHub.Owner(
id: .init(rawValue: 456),
login: "user",
nodeId: "MDQ6VXNlcjQ1Ng==",
avatarUrl: URL(string: "https://github.com/avatar")!,
gravatarId: "",
url: URL(string: "https://api.github.com/users/user")!,
htmlUrl: URL(string: "https://github.com/user")!,
type: "User",
siteAdmin: false
),
htmlUrl: URL(string: "https://github.com/user/test-repo")!,
description: "Test repository",
fork: false,
url: URL(string: "https://api.github.com/repos/user/test-repo")!,
createdAt: Date(),
updatedAt: Date(),
pushedAt: Date(),
size: 100,
stargazersCount: 5,
watchersCount: 3,
language: "Swift",
hasIssues: true,
hasProjects: true,
hasDownloads: true,
hasWiki: true,
hasPages: false,
forksCount: 2,
archived: false,
disabled: false,
openIssuesCount: 1,
license: nil,
allowForking: true,
isTemplate: false,
topics: ["swift", "testing"],
visibility: "public",
defaultBranch: "main"
)
// Create mock client
let client = GitHub.Repositories.Client(
list: { request in
#expect(request?.type == .owner)
return [mockRepo]
},
get: { owner, repo in
#expect(owner == "user")
#expect(repo == "test-repo")
return mockRepo
},
create: { request in
#expect(request.name == "new-repo")
return mockRepo
},
update: { owner, repo, request in
return mockRepo
},
delete: { owner, repo in
return GitHub.Repositories.Delete.Response(
message: "Repository deleted",
documentationUrl: nil
)
}
)
// Test list operation
let repos = try await client.list(GitHub.Repositories.List.Request(type: .owner))
#expect(repos.count == 1)
#expect(repos[0].name == "test-repo")
// Test get operation
let repo = try await client.get(owner: "user", repo: "test-repo")
#expect(repo.fullName == "user/test-repo")
}Each client has a corresponding API router for URL construction:
import GitHub_Repositories_Types
let router = GitHub.Repositories.API.Router()
// Generate URLs for API endpoints
let listAPI = GitHub.Repositories.API.list(request: nil)
let listURL = router.url(for: listAPI)
// Result: /user/repos
let getAPI = GitHub.Repositories.API.get(owner: "coenttb", repo: "swift-github-types")
let getURL = router.url(for: getAPI)
// Result: /repos/coenttb/swift-github-types- Repository view counts and unique visitors (daily/weekly breakdown)
- Clone statistics with unique cloners (daily/weekly breakdown)
- Top referral paths with view counts
- Top referral sources with traffic data
- List repositories with filtering (visibility, type, affiliation)
- Get repository details with full metadata
- Create repositories with templates and configuration
- Update repository settings
- Delete repositories
- List stargazers with pagination
- Stargazer timestamps for analytics
- User information for each stargazer
- List repository collaborators
- Check collaborator status
- Add/remove collaborators
- Manage repository permissions
- Handle collaboration invitations
- swift-types-foundation: A Swift package bundling essential type-safe packages for domain modeling.
- swift-github-live: A Swift package with live implementations for the GitHub API.
- swift-identities-github: A Swift package integrating GitHub OAuth with swift-identities.
- pointfreeco/swift-dependencies: A dependency management library for controlling dependencies in Swift.
- pointfreeco/swift-tagged: A wrapper type for safer, expressive code.
- Swift 6.0+
- macOS 14+ / iOS 17+ / Linux
This package is licensed under the Apache 2.0 License. See LICENSE for details.
For issues, questions, or contributions, please visit the GitHub repository.