Reactions - iOS
When doing a livestream, one of the way to increase the interaction in between the viewer and speaker is by showing the viewers reaction to all. You might have seen emojis flying around during the livestream happening on instagram.
Lets create a similar flying emoji by using the VideoSDK PubSub mechanism.
Step 1: Creating a button to send reaction
When Send is clicked we will send the emoji name to all the participants using the VideoSDK PubSub mechanism and also shows emoji to localParticipant.
We have used SPM package to pick the emoji from below link: https://github.com/Kelvas09/EmojiPicker.git
- Swift
import EmojiPicker
struct LiveStreamView: View {
// for pubsub emojis
@State var selectedEmoji: Emoji?
@State var displayEmojiPicker: Bool = false
var body: some View {
VStack {
Button {
displayEmojiPicker = true
} label: {
Image(systemName: "face.smiling")
.font(.system(size: 20))
.frame(width: 45, height: 45)
.background(Color.white.opacity(0.3))
.clipShape(RoundedRectangle(cornerRadius: 10))
.foregroundColor(.white)
}
.sheet(isPresented: $displayEmojiPicker) {
NavigationView {
EmojiPickerView(
selectedEmoji: $selectedEmoji,
selectedColor: .orange)
.navigationTitle("Select Reaction")
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems(
trailing: Button("Send") {
if let emoji = selectedEmoji {
controller.sendTheReaction(emoji)
}
displayEmojiPicker = false
}
)
}
}
}
}
}
class LiveStreamViewController: ObservableObject {
meeting?.addEventListener(self)
func sendTheReaction(_ emoji: Emoji) {
Task {
do {
try await self.meeting?.pubsub.publish(topic: "REACTION", message: emoji.value, options: [:])
} catch { print("Error while sendTheReaction: \(error)") }
}
}
}
Step 2: Displaying the Reactions to all the users
Here we will listen to the onMessageReceived event of PubSubMessageListener to know someone send the reactions, and show the flying emoji whenever it triggered.
- Swift
import EmojiPicker
import SwiftUI
import VideoSDKRTC
import WebRTC
struct LiveStreamView: View {
@State private var visibleReactions: [Reaction] = []
private let maxVisibleReactions = 13
private let copiesPerReaction = 8
private var reactionOverlay: some View {
GeometryReader { geometry in
ZStack {
ForEach(visibleReactions) { reaction in
Text(reaction.emoji)
.font(.system(size: 40))
.position(
x: geometry.size.width / 2 + reaction.startX,
y: geometry.size.height - 100
)
.modifier(
FloatingAnimation(
finalY: -geometry.size.height + 100,
horizontalOffset: reaction.horizontalOffset
)
)
}
}
}
}
var body: some View {
VStack {
//...
ZStack {
//...
// Add reaction overlay at the screen level
reactionOverlay
}
}
.onChange(
of: controller.reactions,
{ oldValue, newValue in
if let latestReaction = newValue.last {
addReaction(latestReaction)
}
}
)
}
private func addReaction(_ emoji: String) {
for _ in 0..<copiesPerReaction {
let (horizontalOffset, startX) = randomOffset()
let newReaction = Reaction(
id: UUID(),
emoji: emoji,
startX: startX,
horizontalOffset: horizontalOffset
)
withAnimation {
visibleReactions.append(newReaction)
DispatchQueue.main.asyncAfter(
deadline: .now() + Double.random(in: 2.5...3.5)
) {
withAnimation {
visibleReactions.removeAll { $0.id == newReaction.id }
}
}
}
}
if visibleReactions.count > maxVisibleReactions {
visibleReactions.removeFirst(
visibleReactions.count - maxVisibleReactions
)
}
}
}
class LiveStreamViewController: ObservableObject {
@Published var reactions: [String] = []
}
extension LiveStreamViewController: PubSubMessageListener {
func onMessageReceived(_ message: VideoSDKRTC.PubSubMessage) {
if (message.topic == "REACTION") {
self.reactions.append(message.message)
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
if let index = self.reactions.firstIndex(
of: message.message
) {
self.reactions.remove(at: index)
}
}
}
}
}
API Reference
The API references for all the methods and events utilised in this guide are provided below.
Got a Question? Ask us on discord

