How To Build a TikTok Clone With SwiftUI
Tags: swift, swiftu, ios, xcode
Published: 2024-06-19 14:54:29



Apps like [TikTok](https://www.tiktok.com/), [Instagram Reels](https://about.instagram.com/blog/announcements/introducing-instagram-reels-announcement), and [YouTube Shorts](https://www.youtube.com/hashtag/shorts) help users engage with their audiences through concise, bite-sized videos. Let's create a TikTok clone, allowing users to browse through short videos of their favorite people, discover new ones, and also record their own videos for others to watch.

## Project Requirements

To follow along with this tutorial, you will need an Xcode installation. It is recommended to [download Xcode](https://developer.apple.com/xcode/) 15 or a later version. Also, to provide access to the user's camera feed in this app, we will use the [Stream Video SDK](https://getstream.io/video/sdk/). The Video SDK allows developers to build FaceTime-style video calling](https://getstream.io/blog/facetime-clone/), Twitch-like content streaming](https://getstream.io/blog/stream-video-twitch-clone/), Zoom-like video conferencing](https://getstream.io/blog/swiftui-video-conferencing-app/), and [audio room](https://getstream.io/video/sdk/ios/tutorial/audio-room/) platforms with reusable components. After creating a new SwiftUI project, you can fetch and install [Stream Video](https://getstream.io/video/) using [Swift Package Manager](https://www.swift.org/documentation/package-manager/) in Xcode. 


## Explore the Sample SwiftUI TikTok Clone App


![Final TikTok clone](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4vqwoy72ulac9uumgfqd.gif)

Before you follow the steps in this tutorial, it will be great to have a look and feel how the finished project works. You can get the final sample app from [GitHub](https://github.com/GetStream/stream-tutorial-projects/tree/main/iOS-SwiftUI/TikTokSwiftUI), open it in Xcode, explore the code structure and discover how it works. The final project already has microphone and camera usage permissions configured. The following section explains how to set those configurations in Xcode. 

## Create and Configure the SwiftUI Project

Please create a new SwiftUI project in Xcode and name it as you like. The sample project in this tutorial uses **TikTokCloneSwiftUI** as the app name. The app will rely on [audio/sound](https://getstream.io/video/docs/ios/ui-cookbook/audio-volume-indicator/) from the user's device and [live video capture](https://getstream.io/video/docs/ios/ui-components/video-renderer/) from the camera feed. Access to these device capabilities (sound and video) requires setting [camera](https://developer.apple.com/documentation/bundleresources/information_property_list/nscamerausagedescription) and [microphone](https://developer.apple.com/documentation/bundleresources/information_property_list/nsmicrophoneusagedescription) usage description privacies in Xcode. Select the app's root folder in the Xcode Project Navigator and click the **Info** tab. Then, click any of the **+** buttons you see when hovering over the items under **Key**. Search through the **Privacy** category and add:

- **Privacy - Microphone Usage Description**
- **Privacy - Camera Usage Description** 


![Privacy descriptions specification](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h4adtckrlq2ya36tnhf1.png)

## Build the TikTok-Like UIs

The app we built in this tutorial will allow short-form video browsing and self-recording using the iOS device's camera. The primary interaction styles include snap-flicking (paging) to watch bite-sized videos and tapping to record a live video as demonstrated in the image below.


![TikTok-Like UIs](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sohln8lhqfcl8i7gax88.png)


When the app launches, it displays the video feeds [TabView](https://developer.apple.com/documentation/swiftui/tabview) with a horizontal paging scroll style. This effect is opposite to that of the original TikTok app, which snap-scrolls vertically. The video feeds use standard SwiftUI views that implement [AVKit](https://developer.apple.com/documentation/avkit/videoplayer). In a later article, we will integrate this feature with [Stream's Activity Feeds](https://getstream.io/activity-feeds/docs/?language=javascript). 


### Create the Looping Videos

This project will have five looping videos that users can cycle through for demonstration. The video feeds can be implemented using [activity feeds](https://getstream.io/activity-feeds/docs/ios-swift/?language=swift) in an actual application.  Since each looping video uses the same Swift code, let's demonstrate it with the following sample code. When you [download](https://github.com/GetStream/stream-tutorial-projects/tree/main/iOS-SwiftUI/TikTokSwiftUI) the Xcode project from GitHub, you will find all the files in it. 

```swift
//
//  FirstVideoView.swift

import SwiftUI
import AVFoundation
import AVKit

struct FirstVideoView: View {
    @State var player = AVPlayer()
    let avPlayer = AVPlayer(url: Bundle.main.url(forResource: "oneDancing", withExtension: "mp4")!)
    
    var body: some View {
        ZStack {
            VideoPlayer(player: avPlayer)
                .scaledToFill()
                .ignoresSafeArea()
                .onAppear {
                    avPlayer.play()
                    avPlayer.actionAtItemEnd = .none
                    NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: avPlayer.currentItem, queue: .main) { (_) in
                        avPlayer.seek(to: .zero)
                        avPlayer.play()
                    }
            }
        }
    }
}

#Preview {
    FirstVideoView()
        .preferredColorScheme(.dark)
}
```

The sample code above creates a SwiftUI video player that loops forever using AVKit and [AVFoundation](https://developer.apple.com/av-foundation/).

## Create the Video Feed Overlays

![Video Feed Overlays](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/beo2pzi4v3wqwal1r05l.png)

As indicated in the image above, each of the looping videos has two groups of views overlaid on it. Let's create `ReactionButtons1View.swift` to display the user's profile, likes, comments, and sharing ability in a vertical stack parent view. This sample code displays the icons on all the videos when you swipe through them. 

```swift
//
//  ReactionButtonsView.swift
//  TikTokCloneSwiftUI

import SwiftUI

struct ReactionButtons1View: View {
    var body: some View {
        VStack(spacing: 24) {
            Button {
                
            } label: {
                Image(.profile1)
                    .resizable()
                    .scaledToFit()
                    .frame(width: 60, height: 60)
                    .clipShape(Circle())
                    .overlay(
                        Circle()
                            .stroke(LinearGradient(gradient: Gradient(colors: [Color.red, Color.blue]), startPoint: .topLeading, endPoint: .bottomTrailing), lineWidth: 2)
                    )
            }
            
            Button {
                
            } label: {
                VStack {
                    Image(systemName: "suit.heart.fill")
                        .font(.title)
                    
                    Text("5K")
                }
                .foregroundStyle(.white)
            }
            
            Button {
                
            } label: {
                VStack {
                    Image(systemName: "message.fill")
                        .font(.title)
                    
                    Text("56")
                }
                .foregroundStyle(.white)
            }
            
            Button {
                
            } label: {
                VStack {
                    Image(systemName: "square.and.arrow.up.fill")
                        .font(.title)
                    
                    Text("Share")
                }
                .foregroundStyle(.white)
            }
        }
        .padding()
    }
}

#Preview {
    ReactionButtons1View()
        .preferredColorScheme(.dark)
}
```


**Create the TikTok-Like Tab Bar**

The tab bar contains five tab items. Only one of the tab items has interactivity, a plus button to launch a live video of users. To create the UI, add `FeedsView.swift` to the project and use the sample code below to fill out its content. 

```swift
//
//  FeedsView.swift
//  TikTokCloneSwiftUI
//
//  Created by Amos Gyamfi on 31.5.2024.
//

import SwiftUI

struct FeedsView: View {
    @State var top = 0
    @State private var isLocalVideoShowing = false
    
    var body: some View {
        NavigationStack {
            ZStack {
                HTabView()
                    .padding(.top, -200)
                
                HStack {
                    Spacer()
                    //ReactionButtons1View()
                }
            }
            .toolbar {
                
                ToolbarItem(placement: .principal) {
                    
                    HStack {
                        Button {
                            self.top = 0
                        } label: {
                            Text("Following")
                                .fontWeight(self.top == 0 ? .bold : .none)
                                .foregroundStyle(self.top == 0 ? .white : .white.opacity(0.5))
                                .padding(.vertical)
                        }
                        .buttonStyle(.plain)
                        
                        Button {
                            self.top = 1
                        } label: {
                            Text("For You")
                                .fontWeight(self.top == 1 ? .bold : .none)
                                .foregroundStyle(self.top == 1 ? .white : .white.opacity(0.5))
                                .padding(.vertical)
                        }
                        .buttonStyle(.plain)
                    }
                }
                
                ToolbarItemGroup {
                    Button {
                        //
                    } label: {
                        Image(systemName: "magnifyingglass")
                    }
                    .buttonStyle(.plain)
                }
                
                ToolbarItemGroup(placement: .bottomBar) {
                    Button {
                        
                    } label: {
                        VStack {
                            Image(systemName:  "house.fill")
                            Text("Home")
                                .font(.caption)
                        }
                    }
                    .buttonStyle(.plain)
                    
                    Spacer()
                    
                    Button {
                        
                    } label: {
                        VStack {
                            Image(systemName:  "person.2")
                            Text("Friends")
                                .font(.caption)
                        }
                    }
                    .buttonStyle(.plain)
                    
                    Spacer()
                    
                    Button {
                        isLocalVideoShowing.toggle()
                    } label: {
                        Image(systemName: "plus.rectangle.fill")
                    }
                    .font(.title3)
                    .buttonStyle(.plain)
                    .foregroundStyle(.black)
                    .padding(EdgeInsets(top: 0, leading: 2, bottom: 0, trailing: 2))
                    .background(LinearGradient(gradient: Gradient(colors: [.teal, .red]), startPoint: .leading, endPoint: .trailing))
                    .cornerRadius(6)
                    .fullScreenCover(isPresented: $isLocalVideoShowing, content: CreateJoinLiveVideo.init)
                    
                    Spacer()
                    
                    Button {
                        
                    } label: {
                        VStack {
                            Image(systemName:  "tray")
                            Text("Inbox")
                                .font(.caption)
                        }
                    }
                    .buttonStyle(.plain)
                    
                    Spacer()
                    
                    Button {
                        
                    } label: {
                        VStack {
                            Image(systemName:  "person")
                            Text("Profile")
                                .font(.caption)
                        }
                    }
                    .buttonStyle(.plain)
                }
            }
        }
        
    }
}

#Preview {
    FeedsView()
        .preferredColorScheme(.dark)
}
```



## Create the Live Video Overlays

![Live Video Overlays](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gcas97dgbf6c0o2oyerw.png)

The live video overlays consist of a video settings view (vertical container), duration options (horizontal container), and recording UI (pink button), as demonstrated in the image above. Create `LiveVideoSettingsView.swift` and substitute its content with the sample code below. 

```swift
//
//  LiveVideoSettingsView.swift
//  TikTokCloneSwiftUI
//
//  Created by Amos Gyamfi on 1.6.2024.
//

import SwiftUI

struct LiveVideoSettingsView: View {
    var body: some View {
        VStack(spacing: 24) {
            Button {
                //
            } label: {
                Image(systemName: "bolt.slash.fill")
            }
            
            Button {
                //
            } label: {
                Image(systemName: "timer")
            }
            
            Button {
                //
            } label: {
                Image(systemName: "camera.filters")
            }
            
            Button {
                //
            } label: {
                Image(systemName: "camera.aperture")
            }
            
            Button {
                //
            } label: {
                Image(systemName: "wand.and.stars")
            }
        }
        .font(.title2)
        .bold()
        .buttonStyle(.plain)
        .padding()
        .background(.quaternary)
        .cornerRadius(32)
    }
}

#Preview {
    LiveVideoSettingsView()
        .preferredColorScheme(.dark)
}
```

Also, create `LiveVideoOptionsView.swift` and replace the template code with the following. 

```swift
//
//  LiveVideoOptionsView.swift
//  TikTokCloneSwiftUI
//
//  Created by Amos Gyamfi on 1.6.2024.
//

import SwiftUI

struct LiveVideoOptionsView: View {
    var body: some View {
        HStack(spacing: 20) {
            Button {
                //
            } label: {
                Text("10m")
            }
            
            Button {
                //
            } label: {
                Text("60s")
            }
            
            Button {
                //
            } label: {
                Text("15s")
            }
            .buttonStyle(.plain)
            .padding(EdgeInsets(top: 4, leading: 8, bottom: 4, trailing: 8))
            .background(.tertiary)
            .cornerRadius(16)
            
            Button {
                //
            } label: {
                Text("Photo")
            }
            
            Button {
                //
            } label: {
                Text("Text")
            }
        }
        .buttonStyle(.plain)
        .padding()
        .background(.quaternary)
        .cornerRadius(32)
    }
}

#Preview {
    LiveVideoOptionsView()
        .preferredColorScheme(.dark)
}
```


The sample code below creates the recording view in `RecordingView.swift`.

```swift
//
//  RecordingView.swift
//  TikTokCloneSwiftUI
//
//  Created by Amos Gyamfi on 2.6.2024.
//

import SwiftUI

struct RecordingView: View {
    var body: some View {
        ZStack {
            Circle()
                .fill(.pink)
                .frame(width: 64, height: 64)
            Circle()
                .stroke(lineWidth: 4)
                .frame(width: 72, height: 72)
        }
    }
}

#Preview {
    RecordingView()
        .preferredColorScheme(.dark)
}
```

Check all the folders in the Xcode Project Navigator for other files related to the app's UI. 

## Install and Configure the Video SDK

Supposing we are building a production app with Stream's [iOS Video SDK](https://getstream.io/video/sdk/ios/), we will need authenticated user credentials from a server. Since the demo app in this tutorial is purposely for development, we will use hard-coded user credentials from the SDK's [video calling tutorial](https://getstream.io/video/sdk/ios/tutorial/video-calling/). You can use the API key of your Stream account and the companion [token generator service](https://getstream.io/chat/docs/react/token_generator/) to generate random users and tokens for development testing. Check out the [get started guide](https://getstream.io/blog/stream-getting-started-guide/) to learn more. 

To access and work with the SDK, we need to install it as a dependency in the Xcode project. Select **File -> Add Package Dependencies…** and paste this URL, https://github.com/GetStream/stream-video-swift, into the search box to install it. 


![Install the Video SDK](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ah4w7uyc509ma9c9kgrq.png)


## Create a Live Video Participant View

For example, when building an [iOS video conferencing app](https://getstream.io/blog/swiftui-video-conferencing-app/) with the Video SDK, you need to render both the local and remote participants' videos and call controls for muting, flipping the camera from front to back, accepting and rejecting calls. However, the above is not a requirement for our TikTokClone. We need to show only the live video of the local participants. Add `ParticipantsView.swift` to the project and fill out its content with the code below. 

```swift
//
//  ParticipantsView.swift
//  TikTokCloneSwiftUI
//
//  Created by Amos Gyamfi on 1.6.2024.
//

import SwiftUI
import StreamVideo
import StreamVideoSwiftUI

struct ParticipantsView: View {
    
    var call: Call
    var participants: [CallParticipant]
    var onChangeTrackVisibility: (CallParticipant?, Bool) -> Void
    
    var body: some View {
        GeometryReader { proxy in
            if !participants.isEmpty {
                ScrollView {
                    LazyVStack {
                        if participants.count == 1, let participant = participants.first {
                            makeCallParticipantView(participant, frame: proxy.frame(in: .global))
                                .frame(width: proxy.size.width, height: proxy.size.height)
                        } else {
                            ForEach(participants) { participant in
                                makeCallParticipantView(participant, frame: proxy.frame(in: .global))
                                    .frame(width: proxy.size.width, height: proxy.size.height / 2)
                            }
                        }
                    }
                }
            } else {
                Color.black
            }
        }
        .edgesIgnoringSafeArea(.all)
    }
    
    @ViewBuilder
    private func makeCallParticipantView(_ participant: CallParticipant, frame: CGRect) -> some View {
        VideoCallParticipantView(
            participant: participant,
            availableFrame: frame,
            contentMode: .scaleAspectFit,
            customData: [:],
            call: call
        )
        .onAppear { onChangeTrackVisibility(participant, true) }
        .onDisappear{ onChangeTrackVisibility(participant, false) }
    }
}

// Floating Participant
struct FloatingParticipantView: View {
    
    var participant: CallParticipant?
    //var size: CGSize = .init(width: 120, height: 120)
    var size: CGSize = .init(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
    
    var body: some View {
        if let participant = participant {
            VStack {
                HStack {
                    Spacer()
                    
                    VideoRendererView(id: participant.id, size: size) { videoRenderer in
                        videoRenderer.handleViewRendering(for: participant, onTrackSizeUpdate: { _, _ in })
                    }
                    .frame(width: size.width, height: size.height)
                    .clipShape(RoundedRectangle(cornerRadius: 8))
                }
                Spacer()
            }
            .padding()
        }
    }
}
```

In summary, the above sample code does the following:
- Imports the required dependencies: The `StreamVideo` dependency is the core SDK. It does not contain any UI, so it is an excellent choice if you want to build a fully custom TikTok-like experience. Our demo app will use the SDK's reusable UI components by importing `StreamVideoSwiftUI`.
- Manages the layout with Geometry Reader.
- Renders and displays the local participants.
- Watches for visibility changes of the participant.

## How to Capture a Device’s Camera Feed

The video SDK allows developers to access and display local and remote participants' device camera feeds when [building a video calling app] like WhatsApp. In the context of our TikTok clone, we will use the SDK's' `VideoRenderer` to display only the local participant's video. We use the [VideoRender](https://getstream.io/video/docs/ios/ui-components/video-renderer/) because our app's use case does not require [CallControls](https://getstream.io/video/docs/ios/ui-components/call/call-controls/). To render audio/video calling experiences consisting of active, incoming, and outgoing call screens, you should use the SDK's `CallContainer`. Visit our documentation to learn more about the [CallContainer](https://getstream.io/video/docs/ios/ui-components/call/call-container/).

To get a live video from the iOS device’s camera feed, add a new Swift file `CreateJoinLiveVideo.swift`. Then, replace the template code with the following. 

```swift
import SwiftUI
import StreamVideo
import StreamVideoSwiftUI

struct CreateJoinLiveVideo: View {
    @State var call: Call
    @ObservedObject var state: CallState
    @State var callCreated: Bool = false
    @State private var isRecording = false
    
    private var client: StreamVideo
    
    private let apiKey: String = "mmhfdzb5evj2" // The API key can be found in the Credentials section
    private let token: String = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiQm9iYV9GZXR0IiwiaXNzIjoiaHR0cHM6Ly9wcm9udG8uZ2V0c3RyZWFtLmlvIiwic3ViIjoidXNlci9Cb2JhX0ZldHQiLCJpYXQiOjE3MTcxNTM5NTEsImV4cCI6MTcxNzc1ODc1Nn0.wLgPJKgrruRC_4gYT7G0Od2MqPrR1KA8DV-cKi0Yd6k" // The Token can be found in the Credentials section
    private let userId: String = "Boba_Fett" // The User Id can be found in the Credentials section
    private let callId: String = "2dAfJGl7wThv" // The CallId can be found in the Credentials section
    @Environment(\.dismiss) private var dismiss
    
    init() {
        let user = User(
            id: userId,
            name: "Martin", // name and imageURL are used in the UI
            imageURL: .init(string: "https://getstream.io/static/2796a305dd07651fcceb4721a94f4505/a3911/martin-mitrevski.webp")
        )
        
        // Initialize Stream Video client
        self.client = StreamVideo(
            apiKey: apiKey,
            user: user,
            token: .init(stringLiteral: token)
        )
        
        // Initialize the call object
        let call = client.call(callType: "default", callId: callId)
        
        self.call = call
        self.state = call.state
    }
    
    var body: some View {
        NavigationStack {
            VStack {
                if callCreated {
                    ZStack {
                        ParticipantsView(
                            call: call,
                            participants: call.state.remoteParticipants,
                            onChangeTrackVisibility: changeTrackVisibility(_:isVisible:)
                        )
                        
                        FloatingParticipantView(participant: call.state.localParticipant)
                        
                        VStack {
                            HStack {
                                Spacer()
                                LiveVideoSettingsView()
                            }
                            .padding(.horizontal, 32)
                            
                            HStack {
                                Spacer()
                                EffectsButtonView()
                                
                                Spacer()
                                
                                Button {
                                    isRecording.toggle()
                                    if isRecording {
                                        func startRecording() {
                                            Task {
                                                try await call.startRecording()
                                            }
                                        }
                                    } else {
                                        func stopRecording() {
                                            Task {
                                                try await call.stopRecording()
                                            }
                                        }
                                    }
                                } label: {
                                    RecordingView()
                                }
                                .buttonStyle(.plain)
                                
                                Spacer()
                                UploadButtonView()
                                Spacer()
                            }
                            .padding(.top, 128)
                        }
                    }
                } else {
                    //Text("loading...")
                    ProgressView()
                }
            }
            .onAppear {
                Task {
                    guard callCreated == false else { return }
                    try await call.join(create: true)
                    callCreated = true
                }
            }
            .toolbar {
                ToolbarItem(placement: .topBarLeading) {
                    Button {
                        dismiss()
                    } label: {
                        Image(systemName: "xmark")
                    }
                    .buttonStyle(.plain)
                }
                
                ToolbarItem(placement: .principal) {
                    Button {
                        
                    } label: {
                        HStack {
                            Image(systemName: "music.quarternote.3")
                            Text("Add sound")
                        }
                        .font(.caption)
                    }
                    .buttonStyle(.plain)
                    .padding(EdgeInsets(top: 8, leading: 10, bottom: 8, trailing: 10))
                    .background(.quaternary)
                    .cornerRadius(8)
                }
                
                ToolbarItemGroup(placement: .topBarTrailing) {
                    Button {
                        
                    } label: {
                        Image(systemName: "arrow.triangle.2.circlepath")
                    }
                    .buttonStyle(.plain)
                }
                
                ToolbarItem(placement: .bottomBar) {
                    LiveVideoOptionsView()
                }
            }
        }
    }
    
    /// Changes the track visibility for a participant (not visible if they go off-screen).
    /// - Parameters:
    ///  - participant: the participant whose track visibility would be changed.
    ///  - isVisible: whether the track should be visible.
    private func changeTrackVisibility(_ participant: CallParticipant?, isVisible: Bool) {
        guard let participant else { return }
        Task {
            await call.changeTrackVisibility(for: participant, isVisible: isVisible)
        }
    }
    
}
```

To summarize the sample code above, we create an instance of the `StreamVideo` client and a user with a hard-coded token and API key. We check to see if a call has not been created, and then we create and join it to display a live video. The method, `call.join(create: true)` allows real-time sound and video transmission. With the above sample code, our TikTok clone app is ready. Let’s implement live video recording in the next step. 

## Add a Recording Functionality


![Image for recording](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ydulk77qm75eev93k8yx.png)

When users start a live video, the SDK provides an easy way to [integrate recording](https://getstream.io/video/docs/ios/advanced/recording/). This feature allows users to record ongoing audio and video activities. To use the SDK's recording functionality, create a state variable to toggle between the user's recording and non-recording states `@State private var isRecording = false`. Then, add a button to start and stop recording. 

```swift
            Button {
                isRecording.toggle()
                if isRecording {
                    func startRecording() {
                        Task {
                            try await call.startRecording()
                        }
                    }
                } else {
                    func stopRecording() {
                        Task {
                            try await call.stopRecording()
                        }
                    }
                }
            } label: {
                RecordingView()
            }
            .buttonStyle(.plain)
```

You can see the recording implementation in `CreateJoinLiveVideo.swift` you added in the previous section. 

Congratulations 👏. You deserve applause for following this step-by-step guide to building a fully functioning TikTok-like live video using SwiftUI and Stream's [iOS Video SDK](https://getstream.io/video/sdk/ios/). 



![A video preview of the final project](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dqspshd5l7d8ohkkfrvz.gif)


## What’s Next?

This tutorial guided you in integrating short-form video support into a SwiftUI app to build a functioning TikTok clone. However, we did not cover the endless scrollable video feeds feature using a third-party activity feeds solution. That will come in a future tutorial. You can easily integrate activity feeds in your app with the [Stream Activity Feeds API](https://getstream.io/activity-feeds/.

Stream’s Video SDK has much more to offer you as a developer. Head to the [iOS docs](https://getstream.io/video/docs/ios/) to learn more about integrating [screen sharing](https://getstream.io/video/docs/ios/advanced/screensharing/), initiating calls from [deep links](https://getstream.io/video/docs/ios/advanced/deeplinking/), [video/ audio filters](https://getstream.io/video/docs/ios/advanced/apply-video-filters/), and more.