Swift package for sending messages to Slack via Incoming Webhooks with full support for Block Kit.
Also check out MattermostKit - A companion package for sending messages to Mattermost with Slack-compatible attachments.
- Modern Swift API - Built with Swift 6, async/await, and strict concurrency
- Type-Safe - Full Codable support with compile-time safety
- Block Kit - Complete support for Slack's Block Kit API
- Flexible - Send simple text messages or rich interactive messages
- macOS 12.0+
- iOS 15.0+
- tvOS 15.0+
- watchOS 8.0+
- Swift 6.0+
Add SlackKit to your Package.swift file:
dependencies:[.package(url:"https://github.com/diegotl/SlackKit.git", from:"1.0.0")]Or add it directly in Xcode:
- File → Add Package Dependencies
- Enter the repository URL
- Select the version rule
import SlackKit
// Create a webhook client
letclient=trySlackWebhookClient.create(
webhookURLString:"https://hooks.slack.com/services/YOUR/WEBHOOK/URL")
// Send a simple message
tryawait client.send(Message(text:"Hello, Slack!"))SlackKit includes a builder API for cleaner, more readable message construction:
// Clean, declarative syntax
letmessage=Message{Header("Deployment Complete!")Section("Build *#123* was deployed to *production*")Divider()SectionBlock(
fields:[.markdown("*Environment:*\nProduction"),.markdown("*Version:*\nv2.4.1")])}tryawait client.send(message)letmessage=Message(text:"Deployment completed successfully!")tryawait client.send(message)letmessage=Message{Header("Deployment Complete!")Section(markdown:"Build *#123* was deployed to *production*")Divider()Section{Field.markdown("*Environment:*\nProduction")Field.markdown("*Version:*\nv2.4.1")Field.markdown("*Duration:*\n5m 32s")Field.markdown("*Status:*\n:white_check_mark: Success")}}tryawait client.send(message)With custom username and icon:
letmessage=Message(
username:"DeployBot",
iconEmoji:":rocket:"){Header("Deployment Complete!")Section("Build *#123* was deployed to *production*")Divider()}tryawait client.send(message)letmessage=Message(text:"Approval required for production deployment"){Section("Deploy to production?")Actions{ButtonElement(text:.plainText("Approve"), style:.primary, value:"approve")ButtonElement(text:.plainText("Reject"), style:.danger, value:"reject")}}tryawait client.send(message)letmessage=Message(
text:"Build results",
attachments:[Attachment(
color:"good",
title:"Build #123",
text:"Succeeded in 5m 32s",
fields:[AttachmentField(title:"Branch", value:"main", short:true),AttachmentField(title:"Commit", value:"abc123", short:true)])])tryawait client.send(message)letmessage=Message(
text:"This is a threaded reply",
threadTimestamp:"1234567890.123456")tryawait client.send(message)Text sections with optional fields:
Section("Some *formatted* text")
// Or with markdown
Section(markdown:"Some *formatted* text")With fields using the result builder:
Section{Field.markdown("*Field 1*\nValue 1")Field.plainText("Field 2")}Large header text:
Header("Important Announcement")Horizontal line divider:
Divider()Display an image:
Image(url:"https://example.com/image.png", altText:"An example image")Interactive buttons:
Actions{ButtonElement(text:.plainText("Click Me"), actionID:"button_1", value:"button_value", style:.primary)}The builder also supports conditionals and loops:
Actions{ButtonElement(text:.plainText("Approve"), actionID:"approve", value:"yes")if needsReview {ButtonElement(text:.plainText("Request Review"), actionID:"review", value:"review")}foroptionin options {ButtonElement(text:.plainText(option), actionID:"opt_\(option)", value: option)}}Contextual information with text and images:
// Simple text context
Context("Created by @john","2 minutes ago")
// Or with elements using the builder
Context{TextContextElement(text:"Created by @john")ImageContextElement(imageURL:"https://example.com/avatar.png", altText:"Avatar")}Input blocks for collecting user input in modals:
Input(
label:"Task description",
element:PlainTextInputElement(placeholder:"Enter task details...", multiline:true))ButtonElement(
text:.plainText("Click Me"),
actionID:"button_1",
value:"button_value",
style:.primary
)StaticSelectElement(placeholder:.plainText("Choose an option")){Option(text:.plainText("Option 1"), value:"opt1")Option(text:.plainText("Option 2"), value:"opt2")}MultiStaticSelectElement(placeholder:.plainText("Select options"), maxSelectedItems:3){Option(text:.plainText("Option 1"), value:"opt1")Option(text:.plainText("Option 2"), value:"opt2")}DatePickerElement(
actionID:"date_picker_1",
placeholder:.plainText("Select a date"))UsersSelectElement(
placeholder:.plainText("Select a user"),
initialUser:"U1234567890")ConversationsSelectElement(
placeholder:.plainText("Select a conversation"),
filter:ConversationFilter(
include:[.public,.private],
excludeBotUsers:true))TextObject.plainText("Simple text", emoji:true)TextObject.markdown("*Bold* and `code`")do{tryawait client.send(message)}catchSlackError.invalidURL(let url){print("Invalid URL: \(url)")}catchSlackError.invalidResponse(let code,let body){print("HTTP \(code): \(body ??"No body")")}catchSlackError.rateLimitExceeded(let retryAfter){print("Rate limited. Retry after \(retryAfter) seconds")}catchSlackError.encodingError(let error){print("Failed to encode message: \(error)")}catchSlackError.networkError(let error){print("Network error: \(error)")}Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.
- Built with Swift
- Uses Slack Block Kit
- Inspired by the official Slack SDKs
