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.
How does the iOS SDK work?
The SDK wraps the hosted Meeto widget in a web view; it is not a native video engine. 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 runs through Meeto's media servers and is encrypted in transit, not end to end.
The upside is size and upkeep: no WebRTC framework in your binary, and calls improve when the hosted widget does. The trade-off is that the call behaves like Meeto in a mobile browser. The screen-share button appears only where the web view offers screen capture to pages, which iPhone web views do not, so people in your app see screens others share rather than share their own. If your users only need to join meetings, the browser link or the Meeto app may be enough.
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. The SwiftUI view does not expose commands; for join, leave or mute buttons, use MeetoWidgetView below or wrap it in your own UIViewRepresentable.
| Field | Purpose |
|---|---|
| key | Required public key mk_live_… |
| roomName | Persistent named room shared by everyone using it, such as support |
| room | Code of an existing open room, such as the slug POST /api/embed/rooms returns to your server; rooms of /api/v1 meetings ask for a password the SDK cannot pass |
| userName / lang / accent | Prefilled display name, interface language and hex accent color |
| autoJoin | Skip the lobby in open-door rooms; default false |
| baseURL | The 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() }
}| Name | Details |
|---|---|
| ready / phase | The widget loaded; phase reports prejoin, room, finished or error |
| joined / left | payload has room and identity; left has reason and durationSec |
| participants / error | payload 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.
Limits and production checklist
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 | Limit |
|---|---|
| Free | Up to 10 participants and 60-minute calls once two people are in |
| Pro | Up to 25 participants; no time limit for up to 2 simultaneous meetings per account |
| Every plan | The SDK is free; the Powered by Meeto badge stays visible |
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
Is MeetoWidget a native WebRTC SDK?
No. It is a WKWebView wrapper 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, but camera and microphone inside a WKWebView need iOS 14.3 or later. From iOS 15 the SDK approves the web view's camera and microphone request itself, so people see only the system permission prompt and not a second one from the page.
Does it add CallKit, push notifications or a background call?
No. The SDK is an in-app call screen. Ringing, CallKit and push notifications are features of the Meeto app itself, not of this package; your app decides when to open the call screen.
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. Rooms of meetings scheduled through /api/v1 ask for a password, which the SDK cannot pass. A roomName room is shared by everyone who uses that name with your key, so pick names that are not guessable for private calls.
Put it to work
Open your Meeto account to set it up, or explore the working product first.