The first Discord API Library to include Voice Support in Native Swift!
SpincordKit is a comprehensive Swift library for interacting with the Discord API, featuring full voice support, real-time gateway connections, and all the tools you need to build a fully fledged Discord Client Alternative. ETF Encoding is not yet supported, but is in the works. It is also the first Swift library to support all Discord voice encryption algorithms.
- π Gateway Connection - Real-time WebSocket connection to Discord
- π΅ Full Voice Support - Complete voice chat implementation with audio encoding
- π Advanced Voice Encryption - All Discord voice encryption algorithms supported
- π‘ Voice Gateway - Dedicated voice WebSocket connections
- π REST API Client - Complete HTTP API wrapper with retry logic
- π¬ DM Channel Caching - Intelligent caching for direct messages
- ποΈ Adjustable Bitrate - Bypass Discord's bitrate limits for premium audio quality
- π± Cross-Platform - Supports macOS 13+ and iOS 16+
Add SpincordKit to your Package.swift file:
dependencies: [
.package(url: "https://github.com/spinnyspiwal/SpincordKit.git", branch: "main")
]Or add it through Xcode:
- File β Add Package Dependencies
- Enter the repository URL
- Click Add Package
- macOS 13.0+ or iOS 16.0+
- Swift 5.9+
- Opus library (for voice encoding)
import SpincordKit
let gateway = DiscordGateway(token: "YOUR_BOT_TOKEN")
// Register event handlers
gateway.registerCallback(event: "READY") { payload in
print("Bot is ready! π")
print("Logged in as: \(gateway.userData.globalName ?? "Unknown")")
}
gateway.registerCallback(event: "MESSAGE_CREATE") { payload in
guard let d = payload["d"] as? [String: Any],
let content = d["content"] as? String,
let channelId = d["channel_id"] as? String else { return }
if content == "!ping" {
gateway.sendMessage(channelId: channelId, content: "Pong! π") { result in
switch result {
case .success: print("Message sent!")
case .failure(let error): print("Error: \(error)")
}
}
}
}
// Connect to Discord
Task {
await gateway.connect()
}import SpincordKit
// Connect to a voice channel
await gateway.connectToVoice(guildId: "GUILD_ID", channelId: "VOICE_CHANNEL_ID")
// Register voice connection callback
gateway.registerCallback(event: "CONNECTED") { payload in
print("Connected to voice channel! π€")
// Start speaking
Task {
await gateway.voiceSession?.sendSpeakingEvent(isSpeaking: true)
}
}
// Send audio data (PCM format)
if let voiceSession = gateway.voiceSession {
let pcmData = loadAudioData() // Your PCM audio data
let encryptor = VoiceEncryption(
secretKey: secretKey,
mode: .xsalsa20Poly1305Lite
)
Task {
await voiceSession.privateData.udpVoiceConn?.sendAudioPacket(
pcmData,
frameSize: 960,
encryptor: encryptor
)
}
}// Send a message
gateway.sendMessage(channelId: "CHANNEL_ID", content: "Hello World!") { result in
switch result {
case .success(let response):
print("Message sent: \(response)")
case .failure(let error):
print("Failed to send message: \(error)")
}
}
// Get channel messages
gateway.getChannelMessages(
channelId: "CHANNEL_ID",
limit: 50
) { result in
switch result {
case .success(let messages):
print("Retrieved \(messages) messages")
case .failure(let error):
print("Error fetching messages: \(error)")
}
}
// Get DM channels (when you fetch a channel, it will be intelligently cached.)
gateway.getDMChannels { result in
switch result {
case .success(let channels):
print("DM Channels: \(channels)")
case .failure(let error):
print("Error: \(error)")
}
}SpincordKit supports all Discord voice encryption algorithms:
aead_aes256_gcm- AES-256-GCM AEAD encryptionaead_aes256_gcm_rtpsize- AES-256-GCM with RTP size suffixaead_xchacha20_poly1305_rtpsize- XChaCha20-Poly1305 with RTP sizexsalsa20_poly1305- XSalsa20-Poly1305 (standard)xsalsa20_poly1305_suffix- XSalsa20-Poly1305 with suffixxsalsa20_poly1305_lite- Lightweight XSalsa20-Poly1305xsalsa20_poly1305_lite_rtpsize- Lite version with RTP size
// Configure Opus encoder
let encoder = try DiscordOpusEncoder(
sampleRate: 48000,
channels: 2,
frameSize: 960,
bitrate: 128000 // High quality bitrate
)
// Encode PCM audio
let pcmData = loadPCMAudio()
let encodedAudio = try encoder.encode(pcm: pcmData)// Convert Float32 to Int16
let int16Data = PCMFormatter.convertFloat32ToInt16(float32Data)
// Ensure stereo format
let stereoData = PCMFormatter.ensureStereo(monoData, inputChannels: 1)
// Send with automatic format conversion
await udpConnection.sendAudioPacketWithFormat(
audioData,
frameSize: 960,
format: .float32,
inputChannels: 1,
encryptor: encryptor
)let activity = Activity(
name: "π΅ SpincordKit",
type: 0, // Playing
state: "Building awesome bots!"
)
await gateway.updatePresence(
status: "online",
afk: false,
since: 0,
activities: [activity]
)// Handle various Discord events
gateway.registerCallback(event: "GUILD_CREATE") { payload in
// Handle guild events
}
gateway.registerCallback(event: "VOICE_STATE_UPDATE") { payload in
// Handle voice state changes
}
gateway.registerCallback(event: "MESSAGE_DELETE") { payload in
// Handle message deletions
}// Register error callbacks
gateway.registerCallback(event: "ERROR") { payload in
print("Gateway error: \(payload)")
}
// Handle connection issues
gateway.registerCallback(event: "RECONNECT") { payload in
print("Reconnecting to Discord...")
}// Custom voice configuration
let config = VoiceChatConfig(
sampleRate: 48000,
channels: 2,
frameSize: 960,
bitrate: 256000 // High quality
)// Enable verbose logging
Log.verbose = true
// Or use the vprint function
vprint("Debug message", someVariable)The main gateway class for Discord connections.
init(token: String)- Initialize with bot tokenconnect()- Connect to Discord gatewaydisconnect()- Disconnect from gatewayregisterCallback(event:_:)- Register event handlerssendMessage(channelId:content:completion:)- Send messagesconnectToVoice(guildId:channelId:)- Connect to voice channels
Voice-specific gateway for audio streaming.
sendSpeakingEvent(isSpeaking:)- Update speaking statusupdateEncodingBitrate(bitrate:)- Change audio qualityregisterCallback(event:_:)- Register voice event handlers
UDP connection for voice data transmission.
sendAudioPacket(_:frameSize:encryptor:)- Send audio datasendSilenceFrames(encryptor:)- Send silence framesdiscoverIP(_:)- Discover external IP for voice
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- Discord for their comprehensive API documentation
- The Swift community for excellent networking libraries
- Contributors to the Opus and Sodium libraries
- Create an issue for bug reports or feature requests
- Join our Discord Server
Made with β€οΈ by SpinnySpiwal