Skip to content

Connection

RevueSDK has no server to connect to. Devices find each other on a peer-to-peer network, and a call starts when a friend accepts your invitation. Getting there takes four steps:

  1. Start the SDK with Revue.initialize(_:), passing your license and the bootstrap nodes.
  2. Become friends with the person you want to call.
  3. One side invites, the other accepts.
  4. Both sides receive participantDidConnect: the two of you are now connected.

There is only one room, Room.shared. You never create or join a room; friends who accept your invitation show up in it.

Start the SDK

Call Revue.initialize(_:) once, with a Revue.Configuration that has at least these two values:

import RevueSDK
import Foundation

/// Start the SDK with your license file and the bootstrap nodes, then register your delegates.
@MainActor
func startRevue(revueDelegate: RevueDelegate, roomDelegate: RoomDelegate) throws {
    let licenseURL = Bundle.main.url(forResource: "revue", withExtension: "lic")
    let config = Revue.Configuration(
        bootstrapNodes: revueBootstrapNodes,
        licenseData: licenseURL.flatMap { try? Data(contentsOf: $0) }
    )
    // Throws RevueInitError, for example when the license is missing or has expired.
    try Revue.shared.initialize(config)

    // Register after every initialize: destroy removes all delegates.
    Revue.shared.delegates.add(delegate: revueDelegate)
    Room.shared.delegates.add(delegate: roomDelegate)
}

Register your delegates after initialize, as above. Restarting the SDK, the other configuration values, and localParticipant before the SDK starts are covered in Initialization and configuration.

Bootstrap nodes

These five public nodes are ready to use. Pass all of them; the SDK uses whichever it can reach.

Location Address Port TCP relay port UDP relay port
Taiwan, Taipei 43.213.150.13 33445 33445 33446
Taiwan, Taipei 43.212.93.163 33445 33445 33446
Singapore 18.143.211.178 33445 33445 33446
Hong Kong 18.163.229.39 33445 33445 33446
Japan, Tokyo 13.158.239.118 33445 33445 33446

port is always used to join the network. tcpRelayPort and udpRelayPort are optional and default to nil: leave one out and the SDK does not use that node as that kind of relay. Without any TCP relay, devices on networks that block UDP cannot connect.

With their public keys:

import RevueSDK

/// Five public bootstrap nodes. The SDK has no built-in list: pass these, or your own, in `bootstrapNodes`.
let revueBootstrapNodes: [Revue.BootstrapNode] = [
    // Taiwan, Taipei
    Revue.BootstrapNode(address: "43.213.150.13", port: 33445,
                        publicKey: "9FF3EA9309646302E4CFF0C6A770490CCE1A61FCFEB04DDDC1886DDB6FF0390B",
                        tcpRelayPort: 33445, udpRelayPort: 33446),
    // Taiwan, Taipei
    Revue.BootstrapNode(address: "43.212.93.163", port: 33445,
                        publicKey: "7B7E856C72078B3896F05CD7AB76CA8840D81785BDFEDCE502867F27557F3D54",
                        tcpRelayPort: 33445, udpRelayPort: 33446),
    // Singapore
    Revue.BootstrapNode(address: "18.143.211.178", port: 33445,
                        publicKey: "67AB39B2F5CA0EA9D1259D12BF1092175B13343CDA5F8E05E15013DA0F77FC7B",
                        tcpRelayPort: 33445, udpRelayPort: 33446),
    // Hong Kong
    Revue.BootstrapNode(address: "18.163.229.39", port: 33445,
                        publicKey: "8FA77DE6925C34EA7CF655B6D99C786B20EB0807AAA8E6BE00673216A8571142",
                        tcpRelayPort: 33445, udpRelayPort: 33446),
    // Japan, Tokyo
    Revue.BootstrapNode(address: "13.158.239.118", port: 33445,
                        publicKey: "80D1F77158FAE08F1C26F3CF83248BB9738CF4FBF242C4299B1040FB992FB40C",
                        tcpRelayPort: 33445, udpRelayPort: 33446),
]

Add a friend

You can only invite friends. To become friends, one side sends a friend request to the other's address (Revue.revueAddress, for example shown as a QR code) with FriendManager.addFriend(address:message:):

import RevueSDK

/// Your own address. Show it to the person who wants to add you, for example as a QR code.
@MainActor
func myRevueAddress() -> String {
    Revue.shared.revueAddress
}

/// Send a friend request to someone else's address.
@MainActor
func sendFriendRequest(to address: String) throws {
    try Revue.shared.friends.addFriend(address: address, message: "Hi, it's Alice")
}

The other side receives the request in its RevueDelegate and accepts it with FriendManager.addFriendNorequest(friendId:):

import RevueSDK

/// The other side of a friend request: ask the user, then accept so the two of you become friends.
final class FriendRequestHandler: RevueDelegate {
    var onFriendRequest: ((String, String) -> Void)?

    func revue(_ revue: Revue, didReceiveFriendRequest friendId: String, message: String) {
        onFriendRequest?(friendId, message)
    }
}

/// The user tapped Accept on the friend request.
@MainActor
func acceptFriendRequest(from friendId: String) throws {
    try Revue.shared.friends.addFriendNorequest(friendId: friendId)
}

Invite and accept

Invite a friend with FriendManager.sendInvitation(friendId:message:). message is free text that the SDK passes along without reading it, for example to say whether this is a video or a voice call. If the invitation cannot be sent, the call throws a FriendCallError and the friend's device does not ring.

import RevueSDK

/// Invite a friend to a call. Their device rings; the call starts when they accept.
@MainActor
func inviteToCall(friendId: String) throws {
    // Throws FriendCallError when the invitation cannot be sent, for example when the friend is not reachable.
    try Revue.shared.friends.sendInvitation(friendId: friendId, message: "video")
}

The friend's RevueDelegate receives the invitation. They accept with FriendManager.acceptInvitation(friendId:) or reject with FriendManager.rejectInvitation(friendId:). If the invitation ends without a call (cancelled, rejected or timed out), both sides receive didEndInvitation with an InvitationEndReason.

import RevueSDK

/// The invited side: show an incoming-call screen, then accept or reject.
final class IncomingCallHandler: RevueDelegate {
    var onIncomingCall: ((String) -> Void)?
    var onInvitationEnded: ((String) -> Void)?

    func revue(_ revue: Revue, friend friendId: String, didReceiveInvitation message: String?) {
        onIncomingCall?(friendId)
    }

    func revue(_ revue: Revue, friend friendId: String, didEndInvitation reason: InvitationEndReason) {
        // The caller cancelled, the invitation timed out, or you rejected it: close the incoming-call screen.
        onInvitationEnded?(friendId)
    }
}

/// The user tapped Accept. If this throws, keep the incoming-call screen so the user can try again.
@MainActor
func acceptCall(from friendId: String) throws {
    try Revue.shared.friends.acceptInvitation(friendId: friendId)
}

/// The user tapped Reject.
@MainActor
func rejectCall(from friendId: String) {
    try? Revue.shared.friends.rejectInvitation(friendId: friendId)
}

Connected

When the invitation is accepted, both sides receive participantDidConnect in their RoomDelegate. From then on the friend is a RemoteParticipant in Room.remoteParticipants, and you can publish tracks to them. Until then, the friend is not in the room at all.

import RevueSDK

/// Both sides get participantDidConnect once the invitation is accepted. The friend is now a remote participant.
final class CallStartedHandler: RoomDelegate {
    var onConnected: ((RemoteParticipant) -> Void)?

    func room(_ room: Room, participantDidConnect participant: RemoteParticipant) {
        // participant is now in Room.shared.remoteParticipants. Publish your tracks to them from here.
        onConnected?(participant)
    }
}

To call more people, invite each of them; every friend who accepts joins the same room. How a call ends is covered in Disconnection.