A comprehensive Swift SDK for interacting with the GitHub API and building custom GitHub Actions.
- GitHub REST API: Complete client with 39+ endpoints organized in 18 categories
- GitHub Actions Core: Complete toolkit for building GitHub Actions in Swift
- Data Models: 63+ Codable models for all GitHub entities
- Async/Await: Modern API with full concurrency support
- Type-Safe: Strong enums and types to prevent errors
- Cross-Platform: Compatible with iOS 16+ and macOS 12.0+
Add the package to your Package.swift:
dependencies: [
.package(url: "https://github.com/devswiftzone/github-toolkit.git", from: "1.0.0")
]Or in Xcode: File → Add Package Dependencies → Enter the repository URL
import Github
// With personal access token
let github = GitHub(accessToken: "ghp_your_token_here")
// Without authentication (public endpoints only)
let github = GitHub(type: .withoutToken)// Get user repositories
let repos = try await github.repositories(ownerID: "octocat")
// Get a specific repository
let repo = try await github.repository(ownerID: "octocat", repositoryName: "Hello-World")
// Create a repository
let newRepo = NewRepository(
name: "new-repo",
description: "My new repository",
private: false
)
try await github.createRepository(request: newRepo)
// Search repositories
let results = try await github.searchRepositories(
query: "swift toolkit",
sort: .stars,
order: .desc
)// List pull requests
let pulls = try await github.pulls(
ownerID: "owner",
repositoryName: "repo",
state: .open
)
// Get a specific PR
let pr = try await github.pull(
ownerID: "owner",
repositoryName: "repo",
number: 123
)// List issues
let issues = try await github.issues(
ownerID: "owner",
repositoryName: "repo",
state: .open
)
// Search issues
let searchResults = try await github.searchIssues(
query: "is:issue is:open label:bug"
)// Get releases
let releases = try await github.releases(
ownerID: "owner",
repositoryName: "repo"
)
// Get latest release
let latest = try await github.latestRelease(
ownerID: "owner",
repositoryName: "repo"
)// Get current user
let me = try await github.me()
// Get a specific user
let user = try await github.user(username: "octocat")
// Search users
let users = try await github.searchUsers(query: "tom", sort: .followers)
// Followers and following
let followers = try await github.followers(username: "octocat")
let following = try await github.following(username: "octocat")// Authorize with GitHub
let authURL = try github.authorize(
clientID: "your_client_id",
redirectURI: "your_redirect_uri",
scopes: [.repo, .user, .gist]
)
// Open in browser
UIApplication.shared.open(authURL)import Core
// Check if running in GitHub Actions
if Core.env.isRunningInGitHubActions() {
print("Running in GitHub Actions!")
}
// Get workflow information
let workflow = Core.env.getWorkflow()
let repository = Core.env.getRepository()
let event = Core.env.getEventName()// Read workflow inputs
let token = try Core.getInput(
"github-token",
options: InputOptions(required: true)
)
let verboseMode = Core.getBooleanInput("verbose")
let tags = Core.getMultilineInput("tags")
// Set outputs
Core.setOutput(name: "status", value: "success")
Core.setOutput(name: "result", value: "42")// Informational messages
Core.info(message: "Processing files...")
Core.debug(message: "Debug info: \(someVariable)")
// Annotations
Core.warning(message: "This endpoint is deprecated", file: "main.swift", line: 42)
Core.error(message: "Validation failed", file: "validator.swift")
Core.notice(message: "Found 3 warnings")
// Group output
Core.startGroup(name: "Installing dependencies")
// ... commands ...
Core.endGroup()
// Or with closure
try Core.group(name: "Running Tests") {
// ... your code here ...
}// Create a Markdown summary for the workflow
let summary = Core.summary
summary
.addHeading("Test Results", level: 1)
.addRaw("Ran **150 tests**", addEOL: true)
.addSeparator()
.addHeading("Statistics", level: 2)
.addList([
"✅ Passed: 145",
"❌ Failed: 5",
"⏭️ Skipped: 0"
])
.addSeparator()
.addHeading("Code Coverage", level: 2)
.addTable([
["Module", "Coverage"],
["Core", "95%"],
["GitHub API", "87%"],
["HttpClient", "100%"]
])
.addSeparator()
.addCodeBlock("""
func testExample() {
XCTAssertEqual(result, expected)
}
""", language: "swift")
// Write to summary file
try summary.write()
// Clear summary
try summary.clear()// Save state between steps
Core.saveState(name: "processedFiles", value: "file1.txt,file2.txt")
// Retrieve state in a later step
if let files = Core.getState(name: "processedFiles") {
print("Processed files: \(files)")
}
// Mark values as secrets (will be masked in logs)
Core.setSecret("my_secret_token")
// Export environment variables
Core.exportVariable(name: "CUSTOM_VAR", value: "custom_value")
// Add to PATH
Core.addPath("/usr/local/custom/bin")// Mark the step as failed
if validationFailed {
Core.setFailed(message: "Validation failed with 5 errors")
// This sets exit code to 1
}Sources/
├── HttpClient/ # Base HTTP client
│ ├── GitHub.swift # Main client
│ ├── AuthorizationType.swift
│ ├── RequestError.swift
│ └── ...
├── Github/ # GitHub API
│ ├── GitHubAPI/ # Endpoints organized by category
│ │ ├── Repositories/
│ │ ├── Pull/
│ │ ├── Issue/
│ │ ├── User/
│ │ ├── Releases/
│ │ └── ...
│ └── Models/ # Data models
│ ├── Repository.swift
│ ├── User.swift
│ ├── Pull.swift
│ └── ...
├── Core/ # GitHub Actions Toolkit
│ ├── Environment.swift
│ ├── Input.swift
│ ├── Output.swift
│ ├── Logger.swift
│ ├── Summary.swift
│ └── ...
└── Github-toolkit/ # Main package
└── Github_toolkit.swift
- List, search, create, update repositories
- Collaborators, forks, stargazers
- Tags, branches, contributors
- Topics, languages
- List, get, search pull requests
- States and reviews
- List, search issues
- Labels, milestones, comments
- List releases and assets
- Get specific release
- User profile
- Followers, following
- User search
- Discussions
- Notifications
- OAuth
- Licenses
- Gitignore templates
- Search (global)
All models implement Codable and use camelCase automatically:
User: Complete user profileRepository: Detailed repository informationPull: Pull Request with metadataIssue: Issue with labels, milestone, etc.Release: Release with assetsBranch,Tag,CollaboratorDiscussion,Notification- And many more...
do {
let repos = try await github.repositories(ownerID: "octocat")
} catch let error as RequestError {
switch error {
case .notFound:
print("User not found")
case .notAuthorized:
print("Not authorized - check your token")
case .validationFailed(let message):
print("Validation error: \(message)")
case .unknown(let statusCode):
print("Unknown error: \(statusCode)")
}
}GitHub has limits on the number of requests you can make per hour. This SDK includes automatic rate limiting handling:
// Configure rate limiting with auto-retry
let options = RateLimitOptions(
autoRetry: true, // Automatically wait when limit is reached
maxRetries: 3, // Maximum number of retries
throwOnLimit: false, // Don't throw error, wait and retry
warningThreshold: 0.8 // Warn when 80% of limit is used
)
let github = GitHub(
accessToken: "your_token",
rateLimitOptions: options
)
// Manually check rate limit before a request
try await github.checkRateLimit()
// Get current rate limit information
if let rateLimit = await github.getCurrentRateLimit() {
print("Remaining: \(rateLimit.remaining)/\(rateLimit.limit)")
print("Resets at: \(rateLimit.reset)")
print("Usage: \(rateLimit.usagePercentage)%")
}
// Get complete rate limit status
let status = try await github.getRateLimitStatus()
print("Core API: \(status.resources.core.remaining)/\(status.resources.core.limit)")
print("Search API: \(status.resources.search.remaining)/\(status.resources.search.limit)")
print("GraphQL API: \(status.resources.graphql.remaining)/\(status.resources.graphql.limit)")The SDK automatically:
- Extracts rate limit information from response headers
- Warns when approaching the limit (configurable)
- Can automatically wait and retry when limit is reached
- Throws informative errors with reset time
- Swift 5.8+
- iOS 16.0+ / macOS 12.0+
- Xcode 14.0+
- swift-http-types - Apple's HTTP types system
Contributions are welcome. Please:
- Fork the project
- Create a feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
This project is licensed under the MIT License. See the LICENSE file for details.
Asiel Cabrera Gonzalez
Want to create your own GitHub Actions using Swift? Check out our comprehensive guide and resources:
- 📖 Complete Guide to Creating GitHub Actions with Swift (Spanish)
- 💡 Basic Action Template - Ready to use as a starting point
The guide includes:
- Step-by-step tutorial
- 3 complete examples (basic action, repo stats, PR validator)
- Best practices and patterns
- Testing and debugging strategies
- Publishing to GitHub Marketplace
Note: The detailed guide is currently available in Spanish. The code examples and templates are universal and easy to follow regardless of language.
- Based on GitHub REST API v3
- Inspired by @actions/core and @actions/github