### Respond to Incoming Messages with Swift Client Source: https://github.com/tigase/martin/blob/master/Documentation/src/main/restructured/Examples.rst This Swift client example demonstrates how to listen for incoming XMPP messages and automatically respond to them. It covers connection, authentication, session establishment, and message handling. ```swift import TigaseClient let client = Client(host: "your.xmpp.host", port: 5222) client.connect() client.authenticate(username: "user@domain.com", password: "password") client.establishSession() // Handle incoming messages and respond client.onMessageReceived = { message in print("Received message from \(message.from.bareJid): \(message.body)") let reply = Message(to: message.from, body: "I received your message.") client.sendMessage(message: reply) } // Keep the client running to receive messages RunLoop.main.run() ``` -------------------------------- ### Send XMPP Message with Swift Client Source: https://github.com/tigase/martin/blob/master/Documentation/src/main/restructured/Examples.rst This Swift client example demonstrates how to send an XMPP message to a specified recipient. It connects to the server, authenticates, establishes a session, and then sends the message. ```swift import TigaseClient let client = Client(host: "your.xmpp.host", port: 5222) client.connect() client.authenticate(username: "sender@domain.com", password: "Pa$$w0rd") client.establishSession() let message = Message(to: "recipient@domain.com", body: "Hello from Tigase Martin!") client.sendMessage(message: message) client.disconnect() ``` -------------------------------- ### Utilize PubSub with Swift Client Source: https://github.com/tigase/martin/blob/master/Documentation/src/main/restructured/Examples.rst This Swift example illustrates how to leverage the Tigase Martin client for Publish-Subscribe (PubSub) operations. It covers creating a PubSub node, publishing items, receiving notifications, retrieving items, and deleting a PubSub node. ```swift import TigaseClient let client = Client(host: "your.xmpp.host", port: 5222) client.connect() client.authenticate(username: "user@domain.com", password: "password") client.establishSession() let pubSubService = "pubsub.your.xmpp.host" let nodeName = "my_test_node" // Create a PubSub node client.createPubSubNode(serviceJid: pubSubService, node: nodeName) // Publish an item client.publishPubSubItem(serviceJid: pubSubService, node: nodeName, itemId: "1", payload: "Some payload") // Receive notifications client.onPubSubNotification = { notification in print("Received PubSub notification for node: \(notification.node)") } // Retrieve items client.retrievePubSubItems(serviceJid: pubSubService, node: nodeName) // Delete a PubSub node client.deletePubSubNode(serviceJid: pubSubService, node: nodeName) // Keep the client running RunLoop.main.run() ``` -------------------------------- ### Initiate XMPP Client Login Source: https://github.com/tigase/martin/blob/master/Documentation/src/main/restructured/Basics.rst Starts the XMPP client's login process, which includes DNS resolution, establishing a TCP connection, and setting up the XMPP stream. This method should be called after all necessary configurations are complete. ```swift client.login(); ``` -------------------------------- ### Manage Presence and Handle Incoming Presences with Swift Client Source: https://github.com/tigase/martin/blob/master/Documentation/src/main/restructured/Examples.rst This Swift example shows how to connect to an XMPP server, set your client's presence to 'Do not disturb' with a status message, and handle incoming presence updates from contacts. This functionality requires a non-empty roster and at least one available contact. ```swift import TigaseClient let client = Client(host: "your.xmpp.host", port: 5222) client.connect() client.authenticate(username: "user@domain.com", password: "password") client.establishSession() // Set presence to 'Do not disturb' client.setPresence(type: .unavailable, show: .away, status: "Do not disturb me!") // Handle incoming presences client.onPresenceReceived = { presence in print("Received presence from: \(presence.from.bareJid)") } // Keep the client running to receive presence updates RunLoop.main.run() ``` -------------------------------- ### Interact with MUC using Swift Client Source: https://github.com/tigase/martin/blob/master/Documentation/src/main/restructured/Examples.rst This Swift example demonstrates how to use the Tigase Martin client to interact with Multi-User Chat (MUC) rooms. It covers joining a room, sending messages to the room, and handling information about room occupants. ```swift import TigaseClient let client = Client(host: "your.xmpp.host", port: 5222) client.connect() client.authenticate(username: "user@domain.com", password: "password") client.establishSession() // Join a MUC room let mucRoom = "roomname@conference.your.xmpp.host" client.joinMucRoom(roomJid: mucRoom, nick: "MyNick") // Send a message to the MUC room client.sendMucMessage(roomJid: mucRoom, body: "Hello room!") // Handle occupant information (example) client.onMucOccupantPresence = { presence in print("\(presence.from.resource) is in \(presence.from.bareJid)") } // Keep the client running RunLoop.main.run() ``` -------------------------------- ### DiscoveryModule: Discover XMPP Server Features and Items (Swift) Source: https://context7.com/tigase/martin/llms.txt This snippet illustrates the usage of the DiscoveryModule for XMPP Service Discovery (XEP-0030). It shows how to discover server features, identities, items, and query specific component information. It also includes an example of monitoring server feature updates using Combine. The Martin library is a key dependency. ```swift import Martin import Combine let client = XMPPClient() let identity = DiscoveryModule.Identity( category: "client", type: "mobile", name: "MyApp v1.0" ) let discoModule = DiscoveryModule(identity: identity) client.modulesManager.register(discoModule) // Discover server features discoModule.discoverServerFeatures { result in switch result { case .success(let info): print("✓ Server features:") for feature in info.features { print(" - (feature)") } print("\nServer identities:") for identity in info.identities { print(" - (identity.category)/(identity.type): (identity.name ?? "")") } case .failure(let error): print("✗ Discovery failed: (error)") } } // Discover items on server let serverJid = JID("example.com") discoModule.getItems(for: serverJid) { result in switch result { case .success(let items): print("Available services:") for item in items.items { print(" \(item.jid): (item.name ?? "")") } case .failure(let error): print("Error: (error)") } } // Query specific component info let componentJid = JID("conference.example.com") discoModule.getInfo(for: componentJid, node: nil) { result in switch result { case .success(let info): if info.features.contains("http://jabber.org/protocol/muc") { print("✓ Component supports multi-user chat") } case .failure(let error): print("Query failed: (error)") } } // Monitor server features using Combine var cancellables = Set() discoModule.$serverDiscoResult.sink { discoResult in print("Server features updated: (discoResult.features.count) features available") }.store(in: &cancellables) ``` -------------------------------- ### Instant Messaging with Combine Publishers in Swift Source: https://context7.com/tigase/martin/llms.txt Demonstrates sending and receiving one-to-one chat messages using the MessageModule. It leverages Combine publishers for real-time message reception and includes examples for sending new messages and replies. Requires the Martin and Combine frameworks. ```swift import Martin import Combine let client = XMPPClient() let chatManager = DefaultChatManager() let messageModule = MessageModule(chatManager: chatManager) client.modulesManager.register(messageModule) // Subscribe to incoming messages using Combine var cancellables = Set() messageModule.messagesPublisher.sink { messageReceived in let message = messageReceived.message let chat = messageReceived.chat print("From: (message.from?.stringValue ?? "unknown")") print("Body: (message.body ?? "")") print("Chat ID: (chat.id)") // Send reply let reply = chat.createMessage(text: "Got your message!") reply.id = UUID().uuidString client.context.writer.write(reply) }.store(in: &cancellables) // Send a new message let recipientJid = BareJID("bob@example.com") if let chat = chatManager.chat(for: client.context, with: recipientJid) ?? chatManager.createChat(for: client.context, with: recipientJid) { let message = chat.createMessage(text: "Hello, Bob!") message.id = UUID().uuidString message.type = .chat client.context.writer.write(message) } ``` -------------------------------- ### Swift: Create and Use JIDs and BareJIDs for XMPP Addresses Source: https://context7.com/tigase/martin/llms.txt Demonstrates creating BareJID (user@domain) and full JID (user@domain/resource) instances in Swift. It covers accessing components like localpart, domain, and resource, performing comparisons, using them in collections (Set, Dictionary), and their Codable support for persistence. ```swift import Martin // Create BareJID (user@domain) let bareJid1 = BareJID("alice@example.com") let bareJid2 = BareJID(localPart: "bob", domain: "example.com") print(bareJid1.stringValue) // "alice@example.com" print(bareJid1.localPart) // Optional("alice") print(bareJid1.domain) // "example.com" // Create full JID (user@domain/resource) let fullJid1 = JID("alice@example.com/mobile") let fullJid2 = JID(bareJid1, resource: "desktop") let fullJid3 = JID(BareJID("charlie@example.com")) print(fullJid1.stringValue) // "alice@example.com/mobile" print(fullJid1.resource) // Optional("mobile") print(fullJid1.bareJid.stringValue) // "alice@example.com" print(fullJid1.withoutResource) // JID without resource part // JID comparison and hashing let jid1 = JID("user@example.com/resource1") let jid2 = JID("user@example.com/resource2") if jid1.bareJid == jid2.bareJid { print("Same user, different resources") } // Use in collections var contactSet: Set = [] contactSet.insert(BareJID("alice@example.com")) contactSet.insert(BareJID("bob@example.com")) var resourceMap: [JID: String] = [:] resourceMap[JID("alice@example.com/mobile")] = "Mobile device" resourceMap[JID("alice@example.com/desktop")] = "Desktop client" // Codable support for persistence struct UserAccount: Codable { let jid: BareJID let nickname: String } let account = UserAccount(jid: BareJID("alice@example.com"), nickname: "Alice") let encoder = JSONEncoder() if let jsonData = try? encoder.encode(account) { // Save to disk or send over network print(String(data: jsonData, encoding: .utf8) ?? "") } ``` -------------------------------- ### Manage XMPP Client Connection and Events in Swift Source: https://context7.com/tigase/martin/llms.txt This Swift code demonstrates how to set up and manage an XMPP client connection using the Martin library. It includes registering essential XMPP modules, configuring user credentials, setting up an event handler for session, message, and roster events, and initiating the connection. The code also shows how to gracefully disconnect. ```swift import Martin import TigaseLogging // Create and configure XMPP client let client = XMPPClient() // Register required modules client.modulesManager.register(StreamFeaturesModule()) client.modulesManager.register(SaslModule()) client.modulesManager.register(ResourceBinderModule()) client.modulesManager.register(SessionEstablishmentModule()) client.modulesManager.register(RosterModule(rosterManager: DefaultRosterManager())) client.modulesManager.register(PresenceModule(store: DefaultPresenceStore())) client.modulesManager.register(MessageModule(chatManager: DefaultChatManager())) client.modulesManager.register(DiscoveryModule()) client.modulesManager.register(PingModule()) // Configure connection credentials client.connectionConfiguration.userJid = BareJID("user@example.com") client.connectionConfiguration.credentials = .password(password: "secretPassword123") // Set up event handler class MyEventHandler: EventHandler { func handle(event: Event) { switch event { case let e as SessionEstablishmentModule.SessionEstablishmentSuccessEvent: print("✓ Connected and authenticated successfully") case let e as MessageModule.MessageReceivedEvent: print("📩 Message from (e.message.from?.stringValue ?? "unknown"): (e.message.body ?? "")") case let e as RosterModule.ItemUpdatedEvent: print("👤 Roster updated: (e.rosterItem.jid)") default: break } } } let handler = MyEventHandler() client.context.eventBus.register(handler, for: SessionEstablishmentModule.SessionEstablishmentSuccessEvent.TYPE, MessageModule.MessageReceivedEvent.TYPE, RosterModule.ItemUpdatedEvent.TYPE ) // Connect to server client.login() // Later, disconnect gracefully _ = client.disconnect() ``` -------------------------------- ### Connect XMPP Client using Swift Concurrency (async/await) Source: https://context7.com/tigase/martin/llms.txt This Swift code snippet illustrates connecting to an XMPP server using the Martin library with modern Swift concurrency (async/await). It demonstrates registering modules, setting credentials, and handling connection and disconnection within a Task, including robust error handling for connection issues. ```swift import Martin Task { let client = XMPPClient() // Register modules client.modulesManager.register(StreamFeaturesModule()) client.modulesManager.register(SaslModule()) client.modulesManager.register(ResourceBinderModule()) client.modulesManager.register(SessionEstablishmentModule()) // Configure credentials client.connectionConfiguration.userJid = BareJID("alice@xmpp.example.org") client.connectionConfiguration.credentials = .password(password: "alicePassword") do { // Connect and wait for authentication try await client.loginAndWait() print("✓ Successfully connected") // Do work... try await Task.sleep(nanoseconds: 60_000_000_000) // 60 seconds // Disconnect gracefully try await client.disconnect() print("✓ Disconnected cleanly") } catch { print("✗ Connection error: (error)") } } ``` -------------------------------- ### Create XMPP Client Instance Source: https://github.com/tigase/martin/blob/master/Documentation/src/main/restructured/Basics.rst Initializes an instance of the XMPPClient class, which is the core implementation for an XMPP client in the TigaseSwift library. This is the first step to utilizing the library's functionalities. ```swift var client = XMPPClient(); ``` -------------------------------- ### Swift XMPP Connection Testing (XEP-0199) with Martin Source: https://context7.com/tigase/martin/llms.txt Demonstrates using the PingModule in Swift to test XMPP connection liveness and measure round-trip times. It shows how to ping the server, ping a contact using async/await, and implement periodic keepalive pings to detect connectivity issues. Requires the Martin library. ```swift import Martin let client = XMPPClient() let pingModule = PingModule() client.modulesManager.register(pingModule) // Ping the server let serverJid = JID(client.connectionConfiguration.userJid.domain) pingModule.ping(serverJid, timeout: 30.0) { result in switch result { case .success: print("✓ Server is responding") case .failure(let error): print("✗ Ping failed: (error)") } } // Ping another user using async/await Task { let contactJid = JID("bob@example.com") do { try await pingModule.ping(contactJid) print("✓ Contact is online and responding") } catch { print("✗ Contact not responding: (error)") } } // Implement periodic keepalive Timer.scheduledTimer(withTimeInterval: 60.0, repeats: true) { _ in pingModule.ping(serverJid) { result in if case .failure = result { print("⚠️ Server connectivity issue detected") } } } ``` -------------------------------- ### Swift HTTP File Upload (XEP-0363) with Martin Source: https://context7.com/tigase/martin/llms.txt Demonstrates uploading files via HTTP using the HttpFileUploadModule in Swift. It registers the module, discovers an HTTP upload component, requests an upload slot, uploads the file data to the provided PUT URL, and shares the download link via an XMPP message. Dependencies include the Martin library and Foundation. ```swift import Martin import Foundation let client = XMPPClient() let uploadModule = HttpFileUploadModule() client.modulesManager.register(uploadModule) // Find HTTP upload component uploadModule.findHttpUploadComponent { result in switch result { case .success(let components): guard let component = components.first else { print("No upload component available") return } print("✓ Upload component: (component.jid)") print(" Max file size: (component.maxSize) bytes") // Request upload slot let filename = "vacation-photo.jpg" let fileSize = 2_485_760 // bytes let contentType = "image/jpeg" uploadModule.requestUploadSlot( componentJid: component.jid, filename: filename, size: fileSize, contentType: contentType ) { slotResult in switch slotResult { case .success(let slot): print("✓ Upload slot received") print(" PUT URL: (slot.putUri)") print(" GET URL: (slot.getUri)") // Upload file to PUT URL guard let fileData = loadImageData(), let putURL = URL(string: slot.putUri) else { return } var request = URLRequest(url: putURL) request.httpMethod = "PUT" request.httpBody = fileData // Add custom headers from slot for (header, value) in slot.putHeaders { request.setValue(value, forHTTPHeaderField: header) } URLSession.shared.dataTask(with: request) { data, response, error in if let error = error { print("✗ Upload failed: (error)") return } if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 201 { print("✓ File uploaded successfully") // Share download URL in chat let recipientJid = BareJID("friend@example.com") let message = Message() message.to = JID(recipientJid) message.type = .chat message.body = slot.getUri message.id = UUID().uuidString client.context.writer.write(message) } }.resume() case .failure(let error): print("✗ Failed to get upload slot: (error)") } } case .failure(let error): print("✗ Component discovery failed: (error)") } } func loadImageData() -> Data? { // Load your file data here return nil } ``` -------------------------------- ### MucModule: Join and Manage XMPP Chat Rooms (Swift) Source: https://context7.com/tigase/martin/llms.txt This snippet demonstrates how to use the MucModule to join, send messages to, and manage XMPP multi-user chat rooms. It covers room joining, message broadcasting, handling invitations, and retrieving room configuration. Dependencies include the Martin library and Combine for reactive programming. ```swift import Martin import Combine let client = XMPPClient() let roomManager = DefaultRoomManager() let mucModule = MucModule(roomManager: roomManager) client.modulesManager.register(mucModule) // Join a chat room let roomJid = BareJID("developers@conference.example.com") let nickname = "Alice" let password: String? = nil mucModule.join(roomJid: roomJid, nickname: nickname, password: password) { result in switch result { case .success(let room): print("✓ Joined room: (room.jid)") print(" Room name: (room.name ?? "unnamed")") print(" Your nickname: (room.nickname)") // Send message to room let groupMessage = room.createMessage(text: "Hello everyone!") groupMessage.id = UUID().uuidString client.context.writer.write(groupMessage) case .failure(let error): print("✗ Failed to join room: (error)") } } // Subscribe to room messages var cancellables = Set() mucModule.messagesPublisher.sink { messageReceived in let message = messageReceived.message let room = messageReceived.room print("Room: (room.jid)") print("From: (message.from?.resource ?? "unknown")") print("Message: (message.body ?? "")") }.store(in: &cancellables) // Handle room invitations mucModule.inivitationsPublisher.sink { invitation in print("📩 Invitation to room: (invitation.roomJid)") print(" From: (invitation.inviter?.stringValue ?? "unknown")") // Accept invitation mucModule.join( roomJid: invitation.roomJid, nickname: "Alice", password: invitation.password ) { _ in } // Ignore result for brevity }.store(in: &cancellables) // Get room configuration (owners only) if let room = roomManager.room(for: client.context, with: roomJid) { mucModule.getRoomConfiguration(roomJid: JID(room.jid)) { result in switch result { case .success(let config): print("Room configuration loaded") // Modify and save configuration case .failure(let error): print("Failed to get config: (error)") } } // Leave room mucModule.leave(room: room) } ``` -------------------------------- ### Contact List Management with RosterModule in Swift Source: https://context7.com/tigase/martin/llms.txt Provides functionality for managing a user's contact list using the RosterModule. This includes requesting the roster, adding, updating, and removing contacts, as well as subscribing to roster change events via Combine publishers. Requires the Martin framework. ```swift import Martin let client = XMPPClient() let rosterManager = DefaultRosterManager() let rosterModule = RosterModule(rosterManager: rosterManager) client.modulesManager.register(rosterModule) // Request roster from server rosterModule.requestRoster { result in switch result { case .success: print("✓ Roster loaded successfully") // Access roster items let items = rosterManager.items(for: client.context) for item in items { print("Contact: (item.jid) - (item.name ?? "no name")") print(" Subscription: (item.subscription)") print(" Groups: (item.groups)") } case .failure(let error): print("✗ Failed to load roster: (error)") } } // Add a new contact let newContactJid = JID("charlie@example.com") rosterModule.addItem( jid: newContactJid, name: "Charlie", groups: ["Friends", "Work"] ) { result in switch result { case .success: print("✓ Contact added successfully") case .failure(let error): print("✗ Failed to add contact: (error)") } } // Update existing contact rosterModule.updateItem( jid: newContactJid, name: "Charlie Smith", groups: ["Friends"] ) { result in switch result { case .success: print("✓ Contact updated") case .failure(let error): print("✗ Update failed: (error)") } } // Remove contact rosterModule.removeItem(jid: newContactJid) { result in if case .success = result { print("✓ Contact removed") } } // Subscribe to roster changes using Combine var cancellables = Set() rosterModule.events.sink { action in switch action { case .addedOrUpdated(let item): print("👤 Roster item added/updated: (item.jid)") case .removed(let jid): print("👤 Roster item removed: (jid)") } }.store(in: &cancellables) ``` -------------------------------- ### Swift: Create, Parse, and Manipulate XMPP Stanzas Source: https://context7.com/tigase/martin/llms.txt Shows how to construct XMPP stanzas (Message, IQ) in Swift, including setting recipients, senders, types, and adding custom XML elements. It also covers parsing incoming stanzas to extract information like sender, recipient, ID, type, delay information, errors, and child elements. ```swift import Martin // Create custom message stanza let message = Message() message.to = JID("recipient@example.com") message.from = JID("sender@example.com/mobile") message.type = .chat message.id = UUID().uuidString message.body = "Hello, XMPP!" // Add custom XML elements let customElement = Element(name: "custom", xmlns: "http://example.com/custom") customElement.setAttribute("attribute", value: "value") customElement.addChild(Element(name: "data", cdata: "Some data")) message.addChild(customElement) // Parse received stanza func handleIncomingStanza(_ stanza: Stanza) { print("Stanza name: (stanza.name)") print("From: (stanza.from?.stringValue ?? "none")") print("To: (stanza.to?.stringValue ?? "none")") print("ID: (stanza.id ?? "none")") print("Type: (stanza.type?.rawValue ?? "none")") // Check for delay if let delay = stanza.delay { print("Delayed delivery - timestamp: (delay.stamp)") print("Reason: (delay.from?.stringValue ?? "unknown")") } // Parse error stanza if let error = stanza.error { print("Error condition: (error.errorCondition)") print("Error text: (error.text ?? "no description")") } // Find child elements if let customEl = stanza.findChild(name: "custom", xmlns: "http://example.com/custom") { let attributeValue = customEl.getAttribute("attribute") print("Custom attribute: (attributeValue ?? "not found")") } } // Create IQ stanza for queries let iq = Iq() iq.type = .get iq.to = JID("service.example.com") iq.id = UUID().uuidString let query = Element(name: "query", xmlns: "http://jabber.org/protocol/disco#info") iq.addChild(query) // Create result stanza let result = iq.makeResult(type: .result) result.type = .result ``` -------------------------------- ### Send IQ Stanza and Wait for Response (AsyncCallback Protocol) Source: https://github.com/tigase/martin/blob/master/Documentation/src/main/restructured/Basics.rst Sends an IQ stanza and waits for a response using an implementation of the 'AsyncCallback' protocol. This allows for structured handling of asynchronous responses. Supports custom timeouts. ```swift client.context.writer?.write(stanza, timeout: 45, callback: callback); ``` -------------------------------- ### Configure User Credentials for Authentication Source: https://github.com/tigase/martin/blob/master/Documentation/src/main/restructured/Basics.rst Sets the user's JID (Jabber ID) and password within the client's connection configuration for authentication. This is required for most XMPP authentication mechanisms. ```swift let userJID = BareJID("user@domain.com"); client.connectionConfiguration.setUserJID(userJID); client.connectionConfiguration.setUserPassword("Pa$$w0rd"); ``` -------------------------------- ### Send IQ Stanza and Wait for Response (Multiple Callbacks) Source: https://github.com/tigase/martin/blob/master/Documentation/src/main/restructured/Basics.rst Sends an IQ stanza and waits for a specific response, handling success, error conditions, or timeouts via separate callbacks. Supports custom timeouts, with a default of 30 seconds. ```swift client.context.writer?.write(stanza, timeout: 45, onSuccess: {(response) in // response received with type equal `result` }, onError: {(response, errorCondition) in // received response with type equal `error` }, onTimeout: { // no response was received in specified time }); ``` -------------------------------- ### Configure Anonymous Authentication Source: https://github.com/tigase/martin/blob/master/Documentation/src/main/restructured/Basics.rst Configures the client for anonymous authentication by setting only the server domain, without specifying a user JID or password. This allows connecting to servers that support anonymous login. ```swift client.connectionConfiguration.setDomain(domain); ``` -------------------------------- ### Send IQ Stanza and Wait for Response (Single Callback) Source: https://github.com/tigase/martin/blob/master/Documentation/src/main/restructured/Basics.rst Sends an IQ stanza and waits for any response (result, error, or timeout) using a single callback. Distinguishes between response types by checking the received stanza or nil for timeouts. Supports custom timeouts. ```swift client.context.writer?.write(stanza, timeout: 45, callback: {(response) in // will be called on `result`, `error` or in case of timeout }); ``` -------------------------------- ### Send Custom XMPP Stanza Without Response Source: https://github.com/tigase/martin/blob/master/Documentation/src/main/restructured/Basics.rst Sends a custom XMPP stanza to the server without waiting for a reply. This is useful for fire-and-forget messages. Requires an active connection, as 'writer' can be nil. ```swift client.context.writer?.write(stanza); ``` -------------------------------- ### Register StreamFeaturesModule Source: https://github.com/tigase/martin/blob/master/Documentation/src/main/restructured/Basics.rst Registers the StreamFeaturesModule with the client's modules manager. This module is responsible for handling XMPP stream features, which is a fundamental part of the XMPP communication protocol. ```swift client.modulesManager.register(StreamFeaturesModule()); ``` -------------------------------- ### Disconnect Tigase Swift XMPP Client Source: https://github.com/tigase/martin/blob/master/Documentation/src/main/restructured/Basics.rst Properly disconnects from the server, closing both XMPP and TCP connections. This is the standard method for ending a client session. ```swift client.disconnect(); ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.