Build Slack apps in Swift.
Typed Web API, Bolt-style routing, and a SwiftUI-style Block Kit DSL.
Get started · Examples · Documentation · Releases
Tip
2027.1.0-beta.3 is available for testing ahead of the January 2027 release. Changes from 2026.x include:
- Web API response and event models generated from upstream java-slack-sdk, with decoding checked against its recorded payloads.
- Additional Block Kit blocks and elements, including cards, carousels and alerts.
- Agent conversation APIs and events, plus typed Slack Code channel helpers.
AsyncSequencemessage streaming into threads and Code channels, demonstrated in the Codex agent demo.
Beta.3 adds Code channel support and streaming and Socket Mode fixes. See the changelog for details.
APIs may still change. SwiftPM does not select the beta from a 2026.x requirement:
.package(url: "https://github.com/ainame/swift-slack.git", from: "2027.1.0-beta.3")Existing apps should read Migrating to Java-derived models and the changelog for breaking changes. When upgrading from beta.2, note that BlockActionsPayload.team is now optional.
import SlackKit
let router = Router()
// Reply when someone mentions your app
router.onEvent(AppMentionEvent.self) { context, _, event in
guard let channel = event.channel, let user = event.user else { return }
try await context.say(text: "Hi <@\(user)>! 👋", channel: channel)
}
// Handle a slash command
router.onSlashCommand("/echo") { context, payload in
try await context.ack()
try await context.say(text: "Echo: \(payload.text)", channel: payload.channelId)
}
let app = SlackApp(
configuration: .init(appToken: appToken, token: token),
router: router,
mode: .socketMode() // no public URL needed
)
try await app.run()Use as much of the stack as you need:
SlackClient: a standalone Web API client for scripts, CLIs, and CI jobs. No app runtime required.SlackKit: a Bolt-style app framework for interactive apps, with routing, acknowledgements, Socket Mode, and HTTP. It includes the Web API client.
Highlights:
- Over 260 typed Web API methods. Call
chat.postMessage,views.open,conversations.history, and more with Swift request and response models. - Over 90 typed Events API payloads. Register
router.onEvent(AppMentionEvent.self)and receive a decoded struct, not a JSON dictionary. - Socket Mode or HTTP. Connect over WebSocket without a public endpoint, or receive signed HTTP requests in production.
- SwiftUI-style Block Kit DSL. Compose messages, modals, and App Home tabs from reusable Swift views.
- Swift 6 concurrency.
async/awaithandlers,Sendabletypes, andswift-service-lifecycleintegration. - macOS and Linux. CI runs the test suite on Linux, so the same app deploys to servers and containers.
The Web API client is generated with swift-openapi-generator, so it works with any transport from that ecosystem, such as AsyncHTTPClient or URLSession.
Build a bot that replies to /echo using Socket Mode, without a public HTTP endpoint.
Requires Swift 6.2+ and macOS 14+ or Linux, plus a Slack workspace where you can install apps.
Only need Web API calls? Jump to Use the Web API client.
- Create an app and enable Socket Mode.
- Under Basic Information, create an app-level token with the
connections:writescope. - Under OAuth & Permissions, add the bot scopes
commandsandchat:write. - Under Slash Commands, create
/echowith a short description and a usage hint such as[message]. - Install the app to your workspace and copy its Bot User OAuth Token.
mkdir EchoBot
cd EchoBot
mkdir -p Sources/EchoBotSave this as Package.swift:
// swift-tools-version: 6.2
import PackageDescription
let package = Package(
name: "EchoBot",
platforms: [.macOS(.v14)],
dependencies: [
.package(
url: "https://github.com/ainame/swift-slack.git",
from: "2027.1.0-beta.3"
)
],
targets: [
.executableTarget(
name: "EchoBot",
dependencies: [
.product(name: "SlackKit", package: "swift-slack")
]
)
]
)Save this as Sources/EchoBot/main.swift:
import Foundation
import SlackKit
let environment = ProcessInfo.processInfo.environment
guard let token = environment["SLACK_OAUTH_TOKEN"],
let appToken = environment["SLACK_APP_LEVEL_TOKEN"] else {
fatalError("Set SLACK_OAUTH_TOKEN and SLACK_APP_LEVEL_TOKEN")
}
let router = Router()
router.onSlashCommand("/echo") { context, payload in
try await context.ack()
try await context.say(text: "Echo: \(payload.text)", channel: payload.channelId)
}
let app = SlackApp(
configuration: .init(appToken: appToken, token: token),
router: router,
mode: .socketMode()
)
try await app.run()export SLACK_OAUTH_TOKEN="xoxb-your-bot-token"
export SLACK_APP_LEVEL_TOKEN="xapp-your-app-level-token"
swift run EchoBotInvite the bot to a channel, then type /echo Hello from Swift there. The bot replies with Echo: Hello from Swift.
If you have built Slack apps with Bolt for JavaScript or Python, the same concepts apply. A Router registers handlers in place of app, and each handler receives a context with ack(), say(), and the Web API client. Events are acknowledged automatically, as in Bolt.
| Bolt for JavaScript | swift-slack |
|---|---|
app.event('app_mention', ...) |
router.onEvent(AppMentionEvent.self) { ... } |
app.command('/echo', ...) |
router.onSlashCommand("/echo") { ... } |
app.shortcut('callback_id', ...) |
router.onGlobalShortcut("callback_id") { ... } |
app.action('action_id', ...) |
router.onAction("action_id") { ... } |
app.view('callback_id', ...) |
router.onViewSubmission("callback_id") { ... } |
app.options('action_id', ...) |
router.onBlockSuggestion("action_id") { ... } |
await ack() / await say(...) |
try await context.ack() / try await context.say(...) |
client.chat.postMessage({...}) |
context.client.chatPostMessage(body: .json(...)) |
socketMode: true |
mode: .socketMode() |
| Build | Explore |
|---|---|
| Translate messages using flag reactions or shortcuts | DeepL translator |
| Compose interactive messages, modals, and an App Home | Block Kit DSL app |
| Reply to slash commands publicly or privately | Echo bot |
| Handle typed Slack events and interactions | Router example |
| Load external select menu options as the user types | External select |
Browse all examples for more patterns.
Add the SlackBlockKitDSL product to your target to compose messages and modals with result builders:
.product(name: "SlackBlockKitDSL", package: "swift-slack")import SlackBlockKitDSL
import SlackKit
let approval = Section {
Text("Ready to ship?").type(.mrkdwn)
}
.accessory(
Button("Approve").actionId("approve").style(.primary)
)
struct WelcomeModal: SlackModalView {
var title: TextObject { "Welcome" }
var submit: TextObject? { "Continue" }
var blocks: [Block] {
Header { Text("Getting Started") }
Section { Text("Welcome to our app!") }
Input {
PlainTextInput()
.actionId("name_field")
.placeholder("Enter your name")
} label: {
Text("Your Name")
}
}
}Prefer constructing models directly? Use SlackBlockKit. See the Block Kit DSL guide for both styles.
Use SlackClient for direct API access. Add the AsyncHTTPClient transport package to your dependencies and its OpenAPIAsyncHTTPClient product alongside SlackClient in your target. Other transports from the Swift OpenAPI ecosystem also work.
import OpenAPIAsyncHTTPClient
import SlackClient
let slack = Slack(
transport: AsyncHTTPClientTransport(),
configuration: .init(token: token)
)
try await slack.client.chatPostMessage(
body: .json(.init(
channel: "#general",
text: "Hello from Swift!"
))
)See the Web API example for token configuration and a complete entry point.
The default package traits include the Web API, events, and Socket Mode. For smaller builds, select only the traits you need:
.package(
url: "https://github.com/ainame/swift-slack.git",
from: "2027.1.0-beta.3",
traits: ["SocketMode", "Events", "WebAPI_Chat", "WebAPI_Views"]
)SocketMode also enables WebAPI_Apps. For HTTP apps, enable HummingbirdHTTPAdapter and pass a HummingbirdAdapter to SlackApp using mode: .http(adapter). You can also implement HTTPServerAdapter for another server framework.
Events API handlers are acknowledged automatically. Slash commands, block actions, shortcuts, and view handlers must call context.ack(), as in the quickstart.
- Runtime guide: HTTP setup, startup hooks, acknowledgements, and
ServiceGroupintegration. - Package traits: choose the APIs and integrations your app needs.
- API documentation: explore the package's modules and types.
- Migration guide: upgrade from 0.5.x to
SlackApp. - Migrating to Java-derived models: upgrade apps that used
SlackModelsor spell out Web API and event model types.
Issues and pull requests are welcome. If a Web API method or event is missing or decodes incorrectly, open an issue with the payload you received.
To work on the SDK itself, use the Swift and Ruby versions in .swift-version and .ruby-version, plus a C compiler for the tree-sitter Java grammar. The development toolchain currently uses Swift 6.4; the package manifest's minimum is Swift 6.2.
git clone --recursive https://github.com/ainame/swift-slack.git
cd swift-slack
bundle install
make generate
swift testRun make update when intentionally advancing the upstream reference data. See AGENTS.md for the source layout, generator workflow, and contribution checks.
For an agent-reviewed upstream update, invoke $slack-upstream-sync using the
repository's sync skill. It reviews
both vendor deltas, checks generated and handwritten models, and prepares a PR.
The upstream record distinguishes vendor pins from reviewed coverage.
The same Make targets remain available for manual updates; make generate alone
regenerates from the currently checked-out vendor snapshots.
Scheduled Codex runs require full access, authenticated Git/GitHub access, and an
isolated worktree based on current origin/main. Keep the computer and app running.
Test the skill manually before enabling the schedule. GitHub Actions continues to
test PRs; the Schema Update workflow is a manual fallback and should not run while
an agent sync is running or awaiting review.
Web API request parameters come from slack-ruby/slack-api-ref. Response and event models are translated from the model classes of Slack's Java SDK and generated with swift-openapi-generator. Properties other than ok on responses and _type on events are optional, because Slack omits fields depending on the method, the object, and the workspace. Every generated method and event is checked against the Java SDK's recorded payloads (make check-fixtures).
MIT. See LICENSE and THIRD_PARTY_NOTICES.md for upstream attribution.
This is an unofficial, community-maintained project, not affiliated with Slack Technologies, LLC.