iOS Client Example
Before going through this iOS Client example it is recommended to get familiar with the Nabto WebRTC platform using one of the getting started guides which do not require coding.
Example iOS App
You may find the example client app in Nabto WebRTC SDK for iOS. Clone the repository to your local drive and open Demo/Demo.xcodeproj
in xcode. Follow the “run the example test application” instructions in the README.md file to test the application.
iOS Swift does not come with a standard WebRTC implementation. As such the application uses a dependency on WebRTC Binaries for iOS. This is the standard WebRTC implementation precompiled for iOS. This is the same WebRTC library which is also used in Chrome etc.
The other dependency that the application has is of course the Nabto WebRTC SDK. The application is written in Swift and is only about ~300 lines of code total, split among the following 2 files.
PerfectNegotiation.swift
ViewController.swift
The first file implements the Perfect Negotiation strategy for WebRTC. ViewController.swift
contains the one and only ViewController for the whole app.
Note: It is not recommended to structure your own iOS applications in this way. We skip many good practices in order to have a very short and simple to understand example.
It is important to understand the following
- The Nabto SDK for iOS primarily handles the signaling aspect of WebRTC. We do not provide a complete WebRTC solution.
- As stated above, this app uses a standard WebRTC implementation for everything other than signaling.
You may want to use the example app to understand how to use a standard WebRTC library such as Google’s WebRTC. This is out of scope for this guide and may be done at your own discretion. This guide will mostly focus on the usage of the Nabto WebRTC SDK for signaling purposes.
Core concepts
The Nabto WebRTC SDK for iOS is split into two packages, NabtoWebRTC
and NabtoWebRTCUtil
. The NabtoWebRTC
core package contains code that directly interfaces with the Nabto WebRTC platform to establish client connections to devices.
The NabtoWebRTCUtil
package contains code built on top of the core package, including helper classes, classes for signing JSON messages and a standardized signaling message format. You are not required to use our signaling message format, the core package can send any arbitrary JSON across the line. However, this would require you to create a custom message format that your devices and clients agree on. Since we’re using a standard device implementation, we will also be using the standard message format.
SignalingClient
In the core module, a SignalingClient
represents a single logical client connection. A SignalingClient
object is created using the createSignalingClient
utility function. In the ViewController.swift
file you can see how to set up a SignalingClient
object in the ViewDidLoad
function of the ViewController
class.
After creating a SignalingClient, it will be in an inactive state. Usually you would create a class that implements SignalingClientObserver
and add it to the client. This is not done in the example app as we use MessageTransport
to handle observing the SignalingClient.
Once your SignalingClient has been created and you are observing it either using a SignalingClientObserver or MessageTransport, you call the start
method to connect the client to the Nabto platform so you can begin to send and receive signaling messages.
The following code snippet gives a rough sketch of how to create client and start it. MyObserver
is your own class that implements SignalingClientObserver
.
let options = SignalingClientOptions(
productId: "pr-xxxxxx",
deviceId: "de-xxxxxx"
)
let client = createSignalingClient(options)
let observer = MyObserver()
client.addObserver(observer)
client.start();
Now you may send JSON messages back and forth with the device using the sendMessage
method on the client object.
ClientMessageTransport
In the NabtoWebRTCUtil package there is a ClientMessageTransport
class that is made to automate a lot of details away when using our standard message format. This class handles signing messages and sending them through the SignalingClient
object. In the same ViewController.viewDidLoad
method from earlier you can see how it’s set up together with the SignalingClient
object. This makes the complete setup code in the example app the following:
class ViewControlller: UIViewController {
// ...
var signalingClient: SignalingClient? = nil
var messageTransport: MessageTransport? = nil
override func viewDidLoad() {
// ...
signalingClient = createSignalingClient(
SignalingClientOptions(
productId: productId,
deviceId: deviceId
)
)
do {
messageTransport = try createClientMessageTransport(
client: signalingClient!,
options: .sharedSecret(sharedSecret: sharedSecret)
)
messageTransport?.addObserver(self)
try signalingClient?.start()
} catch {
print(error)
}
}
}
extension ViewController: MessageTransportObserver {
func messageTransport(_ transport: any MessageTransport, didGet message: WebrtcSignalingMessage) {
perfectNegotiation.onMessage(message)
}
func messageTransport(_ transport: any MessageTransport, didFinishSetup iceServers: [SignalingIceServer]) {
setupPeerConnection(iceServers)
}
func messageTransport(_ transport: any MessageTransport, didError error: any Error) {
print("MessageTransport error: \(error)")
}
}
In particular, pay attention to the extension that enables the ViewController to be used as an observer for the messageTransport.
The ClientMessageTransport
object receives a message from the device, it will parse that message into a WebrtcSignalingMessage
object which represents any possible signaling message adhering to the message format. The parsed message is then sent to all observers through the didGet message
method of MessageTransportObserver. In our case we pass it on to the PerfectNegotiation
object.
The second observer method, didFinishSetup
, is simply called when the device and client have exchanged “setup” message, notifying each other that they’re both ready to establish a WebRTC connection. didFinishSetup
carries a list of ICE servers which can be used with e.g. Google’s WebRTC to create the actual WebRTC peer connection. In our case this is done in ViewController.setupPeerConnection
.
ClientMessageTransport
also has a method called sendWebrtcSignalingMessage
. Think of this method as being a wrapper around SignalingClient.sendMessage
, and think of the observer didGet message
as being a wrapper around the didGetMessage
method in SignalingClientObserver
.
In this way, the ClientMessageTransport
object helps to ensure that messages are sent and arrive in the same standard format.
The PerfectNegotiation
object is responsible for handling and sending WebrtcSignalingMessages, these are the Descriptions and Candidates which are exchanged between WebRTC peers. For example, when the WebRTC peer connection produces an SDP description that must be sent to the device.
What’s next
With this, the guide is concluded. You may use SignalingClient
and ClientMessageTransport
in your own project as outlined above for a simple on the rails implementation, or you may decide to familiarize yourself more with the SDK and implement your own message format on top of the core module.