# iOS / Swift wrapper for Mediasoup client

**URL:** <https://mediasoup.discourse.group/t/ios-swift-wrapper-for-mediasoup-client/4289>\
**Category:** mediasoup libraries\
**Created:** [June 15, 2022, 5:24am UTC](https://mediasoup.discourse.group/t/ios-swift-wrapper-for-mediasoup-client/4289 "2022-06-15T05:24:22Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![fedulvtubudul](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/fedulvtubudul/32/1128_2.png) [@fedulvtubudul](https://mediasoup.discourse.group/u/fedulvtubudul)\
**Post date:** [June 15, 2022, 5:24am UTC](https://mediasoup.discourse.group/t/ios-swift-wrapper-for-mediasoup-client/4289/1 "2022-06-15T05:24:22Z")

</div>

Hi!

We build a communication platform (server + Web / iOS / Android client apps) based on Mediasoup. While application itself is proprietary, we’ve been using open-source solutions extensively. Regarding iOS client, our first versions were built around @ethand91’s [libmediasoupclient wrapper](https://github.com/ethand91/mediasoup-ios-client). Although there were some issues, it was a good starting point. We’ve managed to fix several bugs that seemed most critical for our application and added couple of features we’ve missed:

- fixed some crashes
- fixed some memory leaks
- added codec factories management
- added TURN servers support

So we’ve lived inside [a fork](https://github.com/VLprojects/mediasoup-ios-client) for some time. But С++ / Objective-C nature of this solution never looked good in a pure Swift project. Not only because of the API style, which could be solved by hiding all the Objective-C into one more layer of abstraction with Swift protocols and enums. There were inherent problems that could be solved only by complete redesign of the C++ to Objective-C bridge itself. First of all, C++ code can throw and all exceptions should be propagated up to the Swift code and never cause the app to crash.

Finally we’ve decided to rewrite all the wrappers (C++ to Objective-C and Objective-C to Swift) from scratch and publish them, so here is our [Mediasoup-Client-Swift](https://github.com/VLprojects/mediasoup-client-swift) wrapper. General idea was to make libmediasoupclient look native in Swift project and as safe as possible (in terms of crashes, exceptions handling and type-safety).

This solutions already runs in our production app and we would like to see more apps using it!

---

<div class="post-metadata">

**Author:** ![ibc](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/ibc/32/1540_2.png) [@ibc](https://mediasoup.discourse.group/u/ibc)\
**Post date:** [November 7, 2022, 12:10am UTC](https://mediasoup.discourse.group/t/ios-swift-wrapper-for-mediasoup-client/4289/2 "2022-11-07T00:10:32Z")

</div>

Sorry for the super late response. Libs added in the website: [mediasoup :: GitHub](https://mediasoup.org/github/#3rd-party-contributions)

Thanks.

---

<div class="post-metadata">

**Author:** ![m-zahmad](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/m-zahmad/32/2760_2.png) [@m-zahmad](https://mediasoup.discourse.group/u/m-zahmad)\
**Post date:** [January 13, 2025, 2:50pm UTC](https://mediasoup.discourse.group/t/ios-swift-wrapper-for-mediasoup-client/4289/3 "2025-01-13T14:50:24Z")

</div>

@fedulvtubudul and @ibc  
I appreciate your efforts and work in these libraries, I am trying to use this swift one, and able to create send transport. From my side audio and video are going to other web app. But receive transport is not working and delegate methods are never getting hit.

Would really appreciate and will be grateful if you can give some direction if I am doing it wrong or is there any step I am missing here.

This is my problem:

> <https://github.com/VLprojects/mediasoup-client-swift/issues/36>
>
> The ReceiveTransport delegate methods (\`onConnect\` and \`onConnectionStateChange\`…) are not being called at all. This issue does not occur with SendTransport, where delegate methods work fine.
> 
> Here is the implementation of \*\*RoomClient\*\* for reference:
> \`\`\`
> 
> final internal class RoomClient {
> ...
> ...
> ...
> func createSendTransport() {
> // Do nothing if send transport is already created
> if (self.sendTransport != nil) {
> print("createSendTransport() send transport is already created...")
> return
> }
>         
> self.createWebRtcTransport(direction: "send")
> print("createSendTransport() send transport created")
> }
>     
> func createRecvTransport() {
> // Do nothing if recv transport is already created
> if (self.recvTransport != nil) {
> print("createRecvTransport() recv transport is already created...")
> return
> }
>         
> self.createWebRtcTransport(direction: "recv")
> print("createRecvTransport() recv transport created")
> }
>   
> ...
> ...
> ...    
> private func createICEServers() -\> String {
> let iceServers: \[\[String: Any\]\] = \[
> \[
> "urls": \["turn server url here"\],
> "username": "user-1",
> "credential": "pass-1"
> \]
> // ,
> // \[
> // "urls": \["stun:stun.l.google.com:19302"\]
> // \]
> \]
>         
> if let jsonData = try? JSONSerialization.data(withJSONObject: iceServers, options: \[\]),
> let jsonString = String(data: jsonData, encoding: .utf8) {
> return jsonString
> }
> return "\[\]"
> }
> 
> private func createWebRtcTransport(direction: String) {
> let reqID = direction == "send" ? SEND\_TRANSPORT\_ID : RECEIVE\_TRANSPORT\_ID
> let isProducing = direction == "send" ? true : false
> let isConsuming = direction == "recv" ? true : false
>         
> // socket call
> let response: JSON = Request.shared.sendCreateWebRtcTransportRequest(socket: self.socket, id: reqID, producing: isProducing, consuming: isConsuming)
> print("createWebRtcTransport() response = " + response.description)
>         
> let webRtcTransportData: JSON = response\["data"\]
>         
> let id: String = webRtcTransportData\["id"\].stringValue
> let iceParameters: JSON = webRtcTransportData\["iceParameters"\]
> let iceCandidatesArray: JSON = webRtcTransportData\["iceCandidates"\]
> let dtlsParameters: JSON = webRtcTransportData\["dtlsParameters"\]
> 
> let iceServers: String = self.createICEServers()
>         
> switch direction {
> case "send":
>             
> print("createWebRtcTransport() iceServers = " + iceServers)
>             
> do {
> self.sendTransport = try self.device.createSendTransport(
> id: id,
> iceParameters: iceParameters.description,
> iceCandidates: iceCandidatesArray.description,
> dtlsParameters: dtlsParameters.description,
> sctpParameters: nil,
> iceServers: iceServers,
> iceTransportPolicy: .all,
> appData: nil)
>                 
> self.sendTransportHandler = SendTransportHandler.init(parent: self)
> self.sendTransport!.delegate = self.sendTransportHandler!
> 
> } catch {
> print("createWebRtcTransport() failed to create send transport " + error.localizedDescription)
> }
>             
> case "recv":
>             
> do {
>                          
> self.recvTransport = try self.device.createReceiveTransport(
> id: id,
> iceParameters: iceParameters.description,
> iceCandidates: iceCandidatesArray.description,
> dtlsParameters: dtlsParameters.description,
> sctpParameters: nil,
> iceServers: iceServers,
> iceTransportPolicy: .all,
> appData: nil)
>                 
> self.recvTransportHandler = RecvTransportHandler.init(parent: self)
> self.recvTransport!.delegate = self.recvTransportHandler!
>                 
> guard let recvTransport = self.recvTransport else {
> print("Receive Transport not created")
> return
> }
> 
> print("Receive Transport created with id: \\(recvTransport.id)")
> 
> // Play consumers that have been stored
> for consumerInfo in self.consumersInfo {
> self.consumeTrack(consumerInfo: consumerInfo)
> }
>                 
> } catch {
> print("createWebRtcTransport() failed to create recv transport " + error.localizedDescription)
> }
>             
> default:
> print("createWebRtcTransport() invalid direction " + direction)
> }
> }
> 
>     
> private func handleLocalTransportConnectEvent(transport: Transport, dtlsParameters: String) {
> print("handleLocalTransportConnectEvent() id =" + transport.id)
> Request.shared.sendConnectWebRtcTransportRequest(socket: self.socket, transportId: transport.id, dtlsParameters: dtlsParameters)
> }
>     
> private func handleLocalTransportProduceEvent(transport: Transport, kind: String, rtpParameters: String, appData: String) -\> String {
> print("handleLocalTransportProduceEvent() id =" + transport.id + " kind = " + kind)
>         
> let transportProduceResponse: JSON = Request.shared.sendProduceWebRtcTransportRequest(socket: self.socket, transportId: transport.id, kind: kind, rtpParameters: rtpParameters, appData: appData)
>         
> return transportProduceResponse\["producerId"\].stringValue
> }
> 
> // Class to handle send transport listener events
> private class SendTransportHandler: SendTransportDelegate {
> private var parent: RoomClient
>         
> init(parent: RoomClient) {
> self.parent = parent
> }
>         
> func onConnect(transport: any Mediasoup.Transport, dtlsParameters: String) {
> print("SendTransport::onConnect dtlsParameters = " + dtlsParameters)
> self.parent.handleLocalTransportConnectEvent(transport: transport, dtlsParameters: dtlsParameters)
> }
>         
> func onConnectionStateChange(transport: any Mediasoup.Transport, connectionState: Mediasoup.TransportConnectionState) {
> print("SendTransport::onConnectionStateChange connectionState = \\(connectionState)")
> }
> 
> func onProduce(transport: any Mediasoup.Transport, kind: Mediasoup.MediaKind, rtpParameters: String, appData: String, callback: @escaping (String?) -\> Void) {
> print("SendTransport::onProduce kind = \\(kind)")
> let kindString = kind == .audio ? "audio" : "video"
> let producerId = self.parent.handleLocalTransportProduceEvent(transport: transport, kind: kindString, rtpParameters: rtpParameters, appData: appData)
>             
> callback(producerId)
> }
>         
> func onProduceData(transport: any Mediasoup.Transport, sctpParameters: String, label: String, protocol dataProtocol: String, appData: String, callback: @escaping (String?) -\> Void) {
> print("SendTransport::onProduceData")
> }
> }
>     
> // Class to handle recv transport listener events
> private class RecvTransportHandler: ReceiveTransportDelegate {
>         
> private var parent: RoomClient
>         
> init(parent: RoomClient) {
> self.parent = parent
> }
>         
> func onConnect(transport: any Mediasoup.Transport, dtlsParameters: String) {
> print("RecvTransport::onConnect")
> self.parent.handleLocalTransportConnectEvent(transport: transport, dtlsParameters: dtlsParameters)
> }
>         
> func onConnectionStateChange(transport: any Mediasoup.Transport, connectionState: Mediasoup.TransportConnectionState) {
> print("RecvTransport::onConnectionStateChange newState = \\(connectionState)")
> }
> }
>     
> // Class to handle producer listener events
> private class ProducerHandler: ProducerDelegate {
> func onTransportClose(in producer: Mediasoup.Producer) {
> print("Producer::onTransportClose")
> }
> }
>     
> // Class to handle consumer listener events
> private class ConsumerHandler: ConsumerDelegate {
> func onTransportClose(in consumer: Mediasoup.Consumer) {
> print("Consumer::onTransportClose")
> }
> }
> }
> \`\`\`
> 
> \*\*Additional Context\*\*
> \- The SendTransport delegate methods work as expected.
> \- Ice servers are configured properly, and WebRTC parameters seem correct.

I can provide further details if you need. Thank you so much and looking forward to hear from you.
