Publish-Subscribe (PubSub)
Publish-Subscribe (PubSub), defined in XEP-0060, is a messaging pattern that allows efficient and scalable distribution of information. It enables entities to publish data to nodes and allows subscribers to receive real-time updates when new data is published.
Overview
PubSub enables:
- Creating and managing pubsub nodes
- Publishing data to nodes
- Subscribing to nodes to receive updates
- Configuring node behavior and access control
- Managing subscriptions
Initializing PubSub
Before using PubSub features, initialize it on your transport:
PubSub.initialize(whixp.transport);
Creating Nodes
Create a new pubsub node:
final result = await PubSub.createNode(
whixp.transport,
JabberID('pubsub.example.com'),
node: 'my-node',
nodeType: PubSubNodeType.leaf,
callback: (iq) {
print('Node created successfully');
},
failureCallback: (error) {
print('Failed to create node: ${error.reason}');
},
);
Node Types
PubSubNodeType.leaf: A leaf node that contains published itemsPubSubNodeType.collection: A collection node that contains other nodes
Publishing to Nodes
Publish data to a node:
final payload = VCard4()..fullName = 'John Doe';
final result = await PubSub.publishVCard(
whixp.transport,
payload,
callback: (iq) {
print('Published successfully');
},
);
Subscribing to Nodes
Subscribe to receive updates from a node:
final result = await PubSub.subscribe(
whixp.transport,
JabberID('pubsub.example.com'),
'my-node',
callback: (iq) {
print('Subscribed successfully');
},
);
Receiving PubSub Events
Handle incoming pubsub events:
PubSub.initialize(whixp.transport);
whixp.addEventHandler<Stanza>('my-node', (stanza) {
if (stanza is VCard4) {
print('Received vCard update: ${stanza.fullName}');
}
});
Node Configuration
Retrieve node configuration:
final config = await PubSub.getNodeConfig(
whixp.transport,
JabberID('pubsub.example.com'),
node: 'my-node',
);
if (config != null) {
// Access configuration fields
for (final field in config.fields) {
print('${field.variable}: ${field.values}');
}
}
Update node configuration:
final config = Form(type: FormType.submit)
..addFields([
Field(variable: 'pubsub#access_model', values: ['open']),
Field(variable: 'pubsub#publish_model', values: ['publishers']),
]);
await PubSub.setNodeConfig(
whixp.transport,
JabberID('pubsub.example.com'),
'my-node',
config,
);
vCard4 Support
PubSub includes built-in support for vCard4 (XEP-0292):
Publishing vCard
final vcard = VCard4()
..fullName = 'John Doe'
..email = 'john@example.com';
await PubSub.publishVCard(whixp.transport, vcard);
Retrieving vCard
final items = await PubSub.retrieveVCard(
whixp.transport,
JabberID('user@example.com'),
);
if (items.isNotEmpty) {
final latest = items.last;
if (latest.payload is VCard4) {
final vcard = latest.payload as VCard4;
print('Name: ${vcard.fullName}');
print('Email: ${vcard.email}');
}
}
Subscribing to vCard Updates
await PubSub.subscribeToVCardUpdates(
whixp.transport,
JabberID('user@example.com'),
);
Unsubscribing from vCard Updates
await PubSub.unsubscribeVCardUpdates(
whixp.transport,
JabberID('user@example.com'),
);
Unsubscribing
Unsubscribe from a node:
await PubSub.unsubscribe(
whixp.transport,
JabberID('pubsub.example.com'),
'my-node',
);
Deleting Nodes
Delete a node (requires owner privileges):
await PubSub.deleteNode(
whixp.transport,
JabberID('pubsub.example.com'),
'my-node',
);
Getting Subscriptions
Retrieve subscriptions for a node:
final result = await PubSub.getNodeSubscriptions(
whixp.transport,
JabberID('pubsub.example.com'),
'my-node',
);
Complete Example
Here's a complete example of using PubSub:
void main() {
final whixp = Whixp(
jabberID: 'user@example.com',
password: 'password',
internalDatabasePath: 'whixp',
);
whixp.addEventHandler('streamNegotiated', (_) async {
// Initialize PubSub
PubSub.initialize(whixp.transport);
// Create a node
await PubSub.createNode(
whixp.transport,
JabberID('pubsub.example.com'),
node: 'my-feeds',
);
// Subscribe to the node
await PubSub.subscribe(
whixp.transport,
JabberID('pubsub.example.com'),
'my-feeds',
);
// Publish data
final customStanza = CustomStanza('Hello, World!');
// ... publish using custom payload
});
// Handle incoming pubsub events
whixp.addEventHandler<Stanza>('my-feeds', (stanza) {
print('Received update: $stanza');
});
whixp.connect();
}
Best Practices
- Initialize first: Always call
PubSub.initialize()before using PubSub features - Handle errors: Always provide error callbacks for pubsub operations
- Manage subscriptions: Keep track of your subscriptions and clean them up when needed
- Use appropriate node types: Choose leaf nodes for data and collection nodes for organization
- Configure access: Set appropriate access models for your nodes based on your use case
PubSub is a powerful feature for building distributed systems, notification services, and real-time data distribution applications.