Skip to content

iOS video call SDK

Add a video call to an iOS app

MeetoWidget is a Swift Package that shows a Meeto video call inside your iOS app. It is a thin wrapper: a WKWebView that loads Meeto's hosted call widget, plus a native bridge for events and commands. You get the same lobby, video grid and chat as on the web, a SwiftUI view and a UIKit view. It builds for iOS 14, needs iOS 14.3 or newer for camera and microphone, and uses a public mk_live_… key.

A Meeto video call ringing on a phone.
On this page

How does the iOS SDK work?

The SDK wraps the hosted Meeto widget in a web view. MeetoWidgetView loads https://meeto.me/embed with your key in a WKWebView, sends commands into the page and turns the page's messages into Swift events. Every call is encrypted in transit (WebRTC, DTLS-SRTP) through Meeto's media servers in Europe and South-East Asia.

The upside is size and upkeep: no WebRTC framework in your binary, and calls improve when the hosted widget does. People in your app get the same lobby, video grid and chat as on the web and see the screens others share in the call. To simply join a meeting, they can also open the browser link or the Meeto app.

Install the Swift Package

Add the package by its repository URL in Xcode, or declare it in Package.swift. The current tag is 0.1.2, and the library product is MeetoWidget.

// Xcode → File → Add Package Dependencies…
// https://github.com/meeto-app/meeto-widget-ios.git

// or in Package.swift:
dependencies: [
    .package(url: "https://github.com/meeto-app/meeto-widget-ios.git", from: "0.1.2")
]

Set up the key and permissions

A native app has no web origin, so the SDK presents itself as app:// plus your bundle identifier. In Settings → Website widget, create a key and add that origin, for example app://com.example.myapp. Without it the widget reports an error instead of loading the call.

Add both usage descriptions to Info.plist. iOS terminates an app that touches the camera or microphone without them.

<key>NSCameraUsageDescription</key>
<string>The camera is used for video calls.</string>
<key>NSMicrophoneUsageDescription</key>
<string>The microphone is used for video calls.</string>

An app origin is declared by the SDK rather than checked by a browser, so treat the mk_live_… key as public, as it is on the web. The server key mk_secret_… never belongs in an app.

Show a call in SwiftUI

MeetoWidget is a SwiftUI view that takes a configuration and an event closure. Give it a frame; the call fills it.

import SwiftUI
import MeetoWidget

struct SupportCallView: View {
    @Binding var isPresented: Bool

    var body: some View {
        MeetoWidget(config: .init(key: "mk_live_…", roomName: "support", userName: "Anna")) { event in
            switch event.type {
            case "joined":
                print("joined room", event.payload["room"] as? String ?? "")
            case "left":
                isPresented = false
            case "error":
                print("Meeto error", event.payload["code"] as? String ?? "")
            default:
                break
            }
        }
        .frame(maxWidth: .infinity, minHeight: 520)
    }
}

The configuration is fixed when the view is created. To switch rooms or keys, give the view a new identity with .id(roomCode) so SwiftUI builds a fresh one. For join, leave or mute buttons, use MeetoWidgetView below, which takes commands, or wrap it in your own UIViewRepresentable.

MeetoWidgetConfig fields
FieldPurpose
keyRequired public key mk_live_…
roomNamePersistent named room shared by everyone using it, such as support
roomCode of an existing open room, such as the slug POST /api/embed/rooms returns to your server
userName / lang / accentPrefilled display name, interface language and hex accent color
autoJoinSkip the lobby in open-door rooms; default false
baseURLThe Meeto address; defaults to https://meeto.me

Use UIKit and send commands

MeetoWidgetView is a UIView that starts loading as soon as you create it. Pin it with constraints and keep a reference for commands.

import UIKit
import MeetoWidget

final class CallViewController: UIViewController {
    private var widget: MeetoWidgetView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // An open room your server created with POST /api/embed/rooms
        widget = MeetoWidgetView(config: .init(key: "mk_live_…", room: "misty-fox-a18f493cb027fe823cb6", userName: "Anna"))
        widget.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(widget)
        NSLayoutConstraint.activate([
            widget.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            widget.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            widget.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            widget.trailingAnchor.constraint(equalTo: view.trailingAnchor),
        ])
        widget.onEvent = { [weak self] event in
            if event.type == "left" { self?.dismiss(animated: true) }
        }
    }

    func muteMe() { widget.setMicrophone(false) }
    func leaveCall() { widget.leave() }
}
Events and commands
NameDetails
ready / phaseThe widget loaded; phase reports prejoin, room, finished or error
joined / leftpayload has room and identity; left has reason and durationSec
participants / errorpayload has count; error has code and message
join(name:) / leave()Join from the lobby of an open-door room, or leave the call
setMicrophone(_:) / setCamera(_:)Turn the microphone or camera on or off during the call

Events arrive on the main thread as MeetoEvent values: a type string and the full payload dictionary of the widget protocol, the same events the JavaScript SDK emits. Commands can be called from any thread.

Production checklist and plan limits

Register the bundle ID

Each app target with its own bundle identifier, including a debug variant, needs its own app:// origin on the key.

Ask for permission first

Show your own explanation before the system camera prompt. If the person declines, the web view gets no camera or microphone.

Test on a device

The Simulator has no real camera. Test a second participant from a browser on another device.

Plan limits for calls in the SDK
PlanLimit
FreeUp to 10 participants and 60-minute calls once two people are in
ProUp to 25 participants; no time limit for up to 2 simultaneous meetings per account
Every planThe SDK is free; the Powered by Meeto badge appears in the call

Limits follow the plan of the account that owns the key. Recording and AI summaries are started by a Pro or Team host; see pricing.

Frequently asked questions

How does MeetoWidget run the call?

In a WKWebView around Meeto's hosted call page, with a Swift bridge for events and commands. Media runs through WebKit's WebRTC and Meeto's media servers, so you do not ship or update a video engine yourself.

Which iOS versions are supported?

The package builds for iOS 14 and later, and camera and microphone inside a WKWebView work from iOS 14.3. From iOS 15 the SDK approves the web view's camera and microphone request itself, so people see only the system permission prompt.

How do I put two app users into the same call?

Give both the same roomName, or create an open room on your server with POST /api/embed/rooms and the secret key, and pass its code to both apps as room. A roomName room is shared by everyone who uses that name with your key, so pick names that are not guessable for private calls.

Try it on your next call

Start a meeting and send the link. Guests join from the browser.

  • 60-minute meetings on the free plan
  • Guests join without an account
Start a meeting

Put it to work

Open your Meeto account to set it up, or explore the working product first.