MQTT iOS frameworks compared: Which to choose in 2026
.jpg?width=4272&height=2500&name=sergey-zolkin-_UeY8aTI6d0-unsplash%20(1).jpg)
Contents
Choosing an MQTT framework for iOS usually comes down to one overlooked factor: which library still gets maintained and actually supports MQTT 5.0 features like reason codes and session expiry. CocoaMQTT, MQTT-Client-Framework, and Moscapsule all claim to do the job, but they diverge sharply on protocol support, background reliability, and setup effort.
This comparison draws on real integration work, including connection recovery timing and QoS delivery testing, to show which framework fits a real-time messaging feature today, not which one merely compiles. For a broader look at how these tradeoffs play out beyond iOS, this breakdown of practical MQTT implementation examples shows how the protocol performs across different real-world systems.
Best MQTT framework for iOS: Quick recommendation
CocoaMQTT is the right default for most iOS teams shipping against MQTT 5.0 today, provided you need native support for features like topic aliasing and enhanced auth.
It handles WebSocket transport, mutual TLS, and ships via both CocoaPods and Swift Package Manager without extra glue code. If you need expert help integrating MQTT into your app, our iOS development services team can guide framework selection and implementation.
MQTT 5.0 became an OASIS standard in 2019, per the OASIS MQTT 5.0 specification, and CocoaMQTT, maintained by EMQX, has tracked it since.
In our work with production iOS clients, Netguru's iOS team has integrated MQTT messaging into shipping apps, benchmarking connection recovery, QoS delivery consistency, and background behavior on iOS 16+. MQTT-Client-Framework remains a fallback for Objective-C codebases stuck pre-MQTT5, but its commit cadence has slowed.
This guide compares both against Moscapsule on broker support, session handling, and error recovery, with code examples.
This kind of production validation reflects rigorous iOS testing practices that teams should apply before shipping MQTT-based features.
MQTT 5.0 support: Which iOS frameworks actually build it
CocoaMQTT is currently the only actively maintained iOS framework with full MQTT 5.0 support, including reason codes, session expiry interval, and shared subscriptions. MQTT-Client-Framework (MQTTClient), by contrast, still targets MQTT 3.1.1 and has seen no substantive protocol updates since its last major release.
The difference shows up in the signed client API. CocoaMQTT5 exposes ConnectProperties directly, letting you set topicAliasMaximum, sessionExpiryInterval, and request problem information at connect time:
let client = CocoaMQTT5(clientID: clientId, host: broker, port: 8883)
client.connectProperties.sessionExpiryInterval = 3600
client.connectProperties.topicAliasMaximum = 10
On disconnect or subscribe failure, CocoaMQTT surfaces the MQTT 5.0 reason code string in the delegate callback rather than a generic error, which matters when you're debugging broker-side rejections (quota exceeded, not authorized, topic filter invalid) in production.
Shared subscriptions, the MQTT 5.0 feature that lets multiple client instances load-balance consumption of a topic, work against CocoaMQTT provided your broker supports the $share/ prefix. MQTTClient has no equivalent path since it predates the spec entirely.
MQTT 5.0 was ratified as an OASIS standard in 2019, per the OASIS MQTT 5.0 specification, and reason codes plus session expiry are core to that spec, not vendor extensions. If your broker (EMQX, HiveMQ, Mosquitto 2.x) already speaks MQTT5, pairing it with a client stuck on 3.1.1 discards half the protocol's value.
CocoaMQTT vs MQTT-client-framework vs moscapsule: Feature comparison
CocoaMQTT, MQTT-Client-Framework (MQTTClient), and Moscapsule solve the same problem with three different engineering philosophies, and only one of them is still actively engineered for iOS 16+ background constraints. Here is how they stack up on the axes that matter for a production integration.
If you're weighing these tradeoffs against a broader roadmap, an expert iOS development team can help you decide which library fits your production constraints.
| CocoaMQTT | MQTT-Client-Framework | Moscapsule | |
|---|---|---|---|
| MQTT 5.0 | Full support (reason codes, session expiry) | 3.1.1 only | 3.1.1 only |
| TLS / mutual TLS | Native URLSession-backed TLS, client-cert auth supported | TLS supported via GCDAsyncSocket, mutual TLS requires manual cert pinning | TLS via underlying Mosquitto C lib, config is manual |
| MQTT over WebSocket | Yes, first-class transport option | Yes, older WebSocket transport implementation | Not supported |
| Maintenance | Active EMQX-backed releases | MQTT-Client-Framework has 83 open issues (GitHub Issues - novastone-media/MQTT-Client-Framework) | Moscapsule (flightonary) GitHub repo has 39 open issues (GitHub - flightonary/Moscapsule Issues, 2024) |
| Dependency size / install | Swift Package Manager or CocoaPods pod, pure Swift, no C bridging | CocoaPods only, Objective-C core with Swift wrapper | Wraps libmosquitto C library, adds a compiled dependency and no SPM path |
QoS levels behave close to spec across all three at QoS 0 and 1, but QoS 2 exactly-once delivery is where Moscapsule's C-library bridging introduces the most session-state drift in our testing, since retry and dedup logic sits outside Swift's control.
CocoaMQTT's Swift-native session and delegate model made it the only client where we could reason about reconnect and Last Will and Testament behavior without stepping into bridged C code.
The OASIS MQTT 5.0 specification, finalized in 2019, defines reason codes and session expiry that only CocoaMQTT exposes through native Swift types like ConnectProperties, MQTT-Client-Framework and Moscapsule both stop at 3.1.1 semantics.
Is each framework still maintained? Release activity check
Moscapsule is not maintained. Its last commit on GitHub predates the widespread adoption of Swift Package Manager, and the repository has gone years without a tagged release; meanwhile, open issues about Boost-based build failures under Xcode 15+ remain unanswered. Moscapsule (flightonary) has 39 open issues on GitHub as of latest check (GitHub - flightonary/Moscapsule Issues page, 2024).
CocoaMQTT tells the opposite story. EMQX ships CocoaMQTT release notes with regular tags tracking MQTT5 delegate changes, ConnectProperties updates, and Swift Package Manager compatibility fixes, and the project has taken commits within the current year according to its GitHub activity graph.
MQTT-Client-Framework sits between the two: still receiving occasional maintainer patches, but with a slower cadence than CocoaMQTT and a growing backlog of unresolved TLS and reconnect issues.
For a production client, this matters more than any feature table. A broker integration with a dead dependency means you inherit every future Xcode toolchain break yourself, with no upstream fix coming. We treat release cadence and open-issue trend as a hard filter before evaluating QoS behavior or transport options at all.
Installing an MQTT client: SPM, CocoaPods, and carthage
Swift Package Manager is the install path EMQX recommends for CocoaMQTT, and it's the one we default to on new iOS builds. Add the dependency directly in Xcode (File > Add Package Dependencies) or in Package.swift:
dependencies: [.package(url: "https://github.com/emqx/CocoaMQTT.git", from: "2.1.0")
]
CocoaPods still works and remains common in older codebases that haven't migrated build systems:
pod 'CocoaMQTT'
Carthage support is thinner. CocoaMQTT's Cartfile entry builds, but release cadence for the Carthage-specific build products lags behind the SPM tag, so pin a commit hash rather than a version range:
github "emqx/CocoaMQTT"
MQTT-Client-Framework (the Objective-C MQTTClient library) ships CocoaPods and Carthage support but no SPM manifest, which rules it out for teams standardizing on Swift Package Manager across their dependency graph. If your team is already SPM-only, that gap alone should narrow the shortlist before you get to QoS behavior or session handling.
MQTT 5.0 in Swift: Connect, subscribe, publish, reconnect
Connecting a CocoaMQTT5 client involves four steps: instantiate with a client ID, set connect properties, attach a Last Will and Testament message, then subscribe and publish over the resulting session. Here's the pattern we use across production iOS builds on MQTT 5.0.
let clientID = "ios-" + String(ProcessInfo.processInfo.globallyUniqueString.prefix(8))
let mqtt5 = CocoaMQTT5(clientID: clientID, host: "broker.example.com", port: 8883)
mqtt5.enableSSL = true
let connectProperties = MqttConnectProperties()
connectProperties.topicAliasMaximum = 10
connectProperties.sessionExpiryInterval = 3600
mqtt5.connectProperties = connectProperties
// Last Will and Testament: broker publishes this if the session dies out
let willMessage = CocoaMQTT5Message(topic: "devices/\(clientID)/status", string: "offline", qos: .qos1)
willMessage.retained = true
mqtt5.willMessage = willMessage
mqtt5.delegate = self
mqtt5.connect
The sessionExpiryInterval and topicAliasMaximum fields only exist because MQTT 5.0 extends the wire protocol with typed connect properties, MQTT 3.1.1 clients can't set either. Once connected, subscribe and publish follow the same delegate pattern:
mqtt5.subscribe("devices/\(clientID)/commands", qos: .qos1)
mqtt5.publish("devices/\(clientID)/telemetry", withString: payload, qos: .qos1, retained: true)
QoS levels govern delivery guarantees, not transport choice: .qos0 is fire-and-forget, .qos1 guarantees at-least-once with possible duplicates, .qos2 guarantees exactly-once at the cost of a four-packet handshake. Per the OASIS MQTT 5.0 specification, the protocol defines three QoS levels, and CocoaMQTT5 exposes all three identically whether the transport is raw TCP or MQTT over WebSocket.
Reconnect logic belongs in mqtt5(_:didStateChangeTo:), not in the connect call. We've seen teams retry connect in a loop and end up with duplicate client IDs fighting the broker for the same session.
Securing the connection: TLS, mutual TLS, and WebSocket setup
Mutual TLS is the baseline we set for any production MQTT deployment on iOS, not an optional hardening step. A one-way TLS handshake proves the broker's identity to the client; mutual TLS adds a client certificate so the broker authenticates the device too, closing the gap that lets a stolen client ID or leaked pod credential reconnect from an unauthorized session.
CocoaMQTT5 exposes this through its sslSettings object. Here's how to configure mutual TLS in CocoaMQTT5:
let mqtt5 = CocoaMQTT5(clientID: clientID, host: "broker.example.com", port: 8883)
mqtt5.enableSSL = true
mqtt5.sslSettings = MQTTSSLSettings(
allowUntrustCACertificate: false,
clientCertificateArray: [clientCert],
isTLSDomainMatchCheck: true
)
MQTT-Client-Framework configures the equivalent through MQTTSSLSecurityPolicy, though its Objective-C bridging makes certificate pinning noticeably more verbose from Swift call sites.
MQTT over WebSocket is the transport to reach for when the network path enforces strict egress rules, corporate proxies and some carrier networks block raw TCP on port 8883 but allow 443.
EMQX documents this as wss:// on port 8084, and both CocoaMQTT and MQTT-Client-Framework support it as a drop-in transport swap with no change to topic, QoS, or Last Will and Testament message handling.
The tradeoff is a small framing overhead per message and one more TLS handshake to budget into your reconnect timeout, particularly relevant if your delegate logic treats a slow handshake as a connection error rather than a retry signal.
Keeping MQTT alive in the iOS background
IOS suspends background sockets within seconds of app backgrounding, so the practical fix is not keeping the connection alive but designing for fast, predictable reconnection. Apple's Background Execution documentation is explicit that BGTaskScheduler grants short, best-effort windows, not a persistent transport, so a raw MQTT socket over WebSocket or TCP will die the moment the OS reclaims memory.
QoS levels are what make that death survivable. QoS 1 guarantees at-least-once delivery on reconnect, QoS 2 adds a handshake that prevents duplicate processing.
Pairing either with a Last Will and Testament message lets the broker notify subscribers the moment a session drops instead of waiting for a timeout.
Reconnection time and message delivery consistency vary across QoS levels when an app moves between background and foreground states on iOS 16 devices, which is exactly what QoS 1 and QoS 2 exist to smooth over.
CocoaMQTT5's connectProperties let you set session expiry and clean-start flags per client, which cut redundant resubscription requests compared with MQTTClient's default session handling.
The practical takeaway: treat backgrounding as an expected disconnect, not an error state, and build your reconnect logic around QoS guarantees rather than fighting the OS for uptime.
FAQ: IOS MQTT framework questions
Which iOS MQTT framework supports MQTT 5.0?
CocoaMQTT vs MQTT-client-framework: Which should I use?
Is moscapsule still maintained?
Does CocoaMQTT support MQTT over WebSocket?
How do I keep an MQTT connection alive when my iOS app is backgrounded?
How do I install an MQTT client via Swift package manager?
What's the best MQTT library for iOS Swift in 2026?
Need help building real-time iOS features?
Choosing between CocoaMQTT, MQTT-Client-Framework, and a custom mqttclient wrapper is only the first decision. Getting QoS handling, mutual TLS, and background reconnection right on iOS 16+ takes real production mileage. If your iOS project also needs Bluetooth connectivity alongside MQTT messaging, comparing iOS BLE frameworks is worth the same scrutiny.
Our iOS engineers have shipped MQTT5 and MQTT over WebSocket integrations that deliver instant answers to end users and keep messaging alive around the clock, even through spotty network handoffs. Whether you need a broker audit, a CocoaMQTT session rewritten for stability, or a full real-time feature built from scratch, talk to our team about your iOS project.
