Monarca is a Swift project designed to consume the BlueSky Firehose using the Jetstream service. It enables real-time data ingestion and processing for applications built on the BlueSky protocol.
- Real-time Data Streaming: Seamlessly consumes BlueSky Firehose streams via Jetstream.
- Swift Integration: Built with Swift, ensuring a fast and native experience.
- Swift 6 Concurrency: Adopts the new Swift concurrency model.
- Lightweight and Efficient: Optimized for performance and low latency.
- Supported collection: Custom types for the following
Committype messagesapp.bsky.feed.likeapp.bsky.graph.followapp.bsky.graph.listitemapp.bsky.actor.profileapp.bsky.graph.blockapp.bsky.feed.postapp.bsky.graph.starterpackapp.bsky.feed.threadgateapp.bsky.feed.postgate
- Swift 6.0 or later
- macOS 13.0+ || iOS 13+
- Internet connection to access the BlueSky Jetstream service
Monarca is distributed using Swift Package Manager so you can include easily in your Swift Packages or Xcode projects.
Include the following line in the dependencies section of your Package.swift file
...
dependencies: [
// π¦ Monarca
.package(url: "https://github.com/fitomad/monarca.git", from: "0.2.0")
]
...- Import Monarca into your Swift project:
import Monarca- Initialize the Firehose client:
Monarca use a Builder pattern to configure and create the Jetstream connection.
The builder is available through the DefaultFirehoseClientBuilder class. You can configure the following Jetstream connection with these builder functions.
- Jetstream server -
connecto(to:): Mandatory. Select one of the four general available Jetstream servers or connect to a custon server using the.custom(server:)case, whereserveris aStringthat contains the URL to your server. wantedCollections-forCollections: AStringarray with the collection's NSID.wantedDids-withDecentralizedIdentifiers: AStringarray with the repo DIDs to filter which records you receive on your stream.compress-compressionEnabled: A boolean value to enablezstdcompression. DefaultfalserequireHello-withHelloExecution: A boolean value to replay/live-tail until the server recevies aSubscriberOptionsUpdatePayloadover the socket in a Subscriber Sourced Message. DefaultfalsemaxMessageSizeBytes-maximumMessageSizeAllowed: Filters by message size. You can set this size using the helper enumerationMessageSizecursor-messagesPlayback(since:): A unix microseconds timestamp cursor to begin playback if you reconnect or connect to a new Jetstream server. You can set the value using the helper enumerationPlayback
This connection reveices all Bluesky messages for all collection and DIDs from the jetstream1 server located in the US east coast. No compression enabled, witho no hello command needed and with no message size limitation.
let bskyFirehoseClient = try await DefaultFirehoseClientBuilder()
.connect(to: .usaEast1)
.build()This example connection uses all the settings available
let firehoseClient = try DefaultFirehoseClientBuilder()
.connect(to: .usaEast1)
.forCollections([ .post, .repost, .like ])
.withDecentralizedIdentifiers(Constants.customIdentifierList)
.compressionEnabled(false)
.withHelloExecution(false)
.maximumMessageSizeAllowed(.kilobytes(value: 2048))
.messagesPlayback(since: .seconds(5))
.dedicatedThreads(count: 8)
.build()await bskyFirehoseClient.onMessageReceived { message in
switch message {
case .account(let payload):
print("π Account")
case .commit:
case .identity(let message):
case .unknown(let data):
}
}You can capture and manage errors from the Jetstream connection using the onErrorProcessingMessage(:_) that receives a BskyFirehoseError error type.
await firehoseClient.onErrorProcessingMessage { bskyFirehoseError in
switch bskyFirehoseError {
case invalidMessage(let websocketMessage):
switch websocketMessage {
case .data(let data):
//
case .string(let string):
@unknown default:
}
case .invalidFirehoseURL:
// The custom Jetstream URL is not valid
case .invalidConnectionParameters:
// The connection parameters are rejected by server
case .invalidData:
case .messageManagerNotAvailable:
// There is no message manager component available
// or the one you provide is not valid.
}
}In case you receive a strange message from Jetstream, you can inspect the whole websoccket response available in the invalidMessage error type, that has an URLSessionWebSocketTask.Message as associated type.
Apart from the parameters specific to the Jetstream connection, you can set up your own message parsing engine. By default, Monarca provides its own parsing engine, so this step is not necessary, but it's left open in case your project has special requirements.
The only requirement is that your parsing engine implements the BskyMessageManager protocol, which will handle receiving messages from the WebSocket connected to Jetstream and return a BskyMessage type message.
struct MockMessageManager: BskyMessageManager {
func processMessage(content data: Data) throws -> BskyMessage {
// Do things with the message here
}
func processMessage(string value: String) throws -> BskyMessage {
guard let data = value.data(using: .utf8) else {
throw MyError.strangeMessage
}
return try await processMessage(content: data)
}
}Once you finish to develop the custom message manager component, use the withMessageManager(_:) function to set your custom component.
let bskyFirehoseClient = try await DefaultFirehoseClientBuilder()
.connect(to: .usaEast1)
.useCustomMessageManager(MockMessageManager)
.build()Now you are ready to receive new messages and process them using your custom manager.
Monarca is licensed under the MIT License. See LICENSE for details.
- Fix parsing of embed video content in a commit
- Unit test description for some of the test suites
- Better connection status information
- New
stopandshutdownfunctions
- Memory management improvements
- Fix a misspelling issue related with
FirehoseHosttype
- Support for the following Commit collection
fm.teal.alpha.actor.statusapp.bsky.notification.declarationapp.bsky.actor.status
- The
BskyCollectionelementprofileis nowactorProfile
- Migrate from the
URLSessionWebSocketTaskto the SwiftNIO based WebSocket connection using the WebSocketKit framework - Renaming some
BskyFirehoseClientBuilderfunctions to bring a Fluid approach to the API. Old funcions are now marked as deprecated and will be removed in future versions. - Work to adopt Swift 6 Concurrency model is still in progress.
- Now you can filter
app.bsky.feed.postmessages by hashtag or by content with the new functions availables in theBskyFirehoseClientBuilderimplementationapplyContentFilter(by:)Process only those messages that contains 1 or N of the given hashtagsapplyHashtagFilter(by:)Process only those messages that contains 1 or N of the given words
- Information about video in a Post message.
- Support for the following Commit collections
app.bsky.feed.threadgateapp.bsky.feed.postgate
- Bluesky Firehose connection using Jetstreams.
- Custom types for the following messages
- Account
- Identity
- Commit
app.bsky.feed.likeapp.bsky.graph.followapp.bsky.graph.listitemapp.bsky.actor.profileapp.bsky.graph.blockapp.bsky.feed.postapp.bsky.graph.starterpack
You can reach me here π