Skip to content
All essays
iOSMarch 27, 202511 min

Swift ReplayKit: Screen Recording and Broadcasting

Implement screen recording and broadcasting with ReplayKit. In-app recording and streaming.

Ü
Ümit Uz
Mobile & Full Stack Developer

ReplayKit enables your app to record screen content and broadcast to streaming platforms. Learn to implement recording features.

ReplayKit Setup

Add ReplayKit Capability

  1. 1Go to Project Settings > Signing & Capabilities
  2. 2Add "ReplayKit" capability
  3. 3Ensure app has screen capture permission

Screen Recording

Basic Recording

swift
import ReplayKit
import AVFoundation

class ScreenRecorderManager: NSObject, RPScreenRecorderDelegate {
    private var recorder: RPScreenRecorder?
    private var writer: AVAssetWriter?
    private var writerInput: AVAssetWriterInput?

    @Published var isRecording = false
    @Published var recordingURL: URL?

    func startRecording() {
        recorder = RPScreenRecorder.shared()

        recorder?.delegate = self
        recorder?.startRecording { [weak self] error in
            if let error = error {
                print("Recording error: \(error.localizedDescription)")
                self?.isRecording = false
            } else {
                self?.isRecording = true
                print("Recording started")
            }
        }
    }

    func stopRecording() {
        recorder?.stopRecording { [weak self] previewViewController in
            self?.isRecording = false

            if let previewViewController = previewViewController {
                // Show preview
                if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
                   let rootViewController = windowScene.windows.first?.rootViewController {
                    rootViewController.present(previewViewController, animated: true)
                }
            }
        }
    }

    func screenRecorderDidChangeAvailability(_ screenRecorder: RPScreenRecorder) {
        // Handle availability changes
    }
}

Video Recording

Save to File

swift
extension ScreenRecorderManager {
    func startRecordingToFile() {
        recorder = RPScreenRecorder.shared()

        recorder?.startCapture { captureHandler in
            let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
            let outputPath = (documentsPath as NSString).appendingPathComponent("screen_recording.mp4")
            let outputURL = URL(fileURLWithPath: outputPath)

            self.setupWriter(outputURL: outputURL)

            captureHandler { sampleBuffer in
                self?.processSampleBuffer(sampleBuffer)
            }
        }
    }

    private func setupWriter(outputURL: URL) {
        let writer = try? AVAssetWriter(outputURL: outputURL, fileType: .mp4)

        let videoSettings: [String: Any] = [
            AVVideoCodecKey: AVVideoCodecType.h264,
            AVVideoWidthKey: UIScreen.main.bounds.size.width,
            AVVideoHeightKey: UIScreen.main.bounds.size.height
        ]

        let writerInput = AVAssetWriterInput(mediaType: AVMediaType.video, outputSettings: videoSettings)

        writer?.add(writerInput)
        writer?.startWriting()

        self.writer = writer
        self.writerInput = writerInput
    }

    private func processSampleBuffer(_ sampleBuffer: CMSampleBuffer) {
        guard let writer = writer,
              let writerInput = writerInput else {
            return
        }

        if writer.status == .unknown {
            writer.startSession(atSourceTime: CMSampleBufferGetPresentationTimeStamp(sampleBuffer))
        }

        if !writerInput.isReadyForMoreMediaData {
            return
        }

        writerInput.append(sampleBuffer)
    }

    func stopRecordingToFile() {
        recorder?.stopCapture()
        writer?.finishWriting {
            print("Recording saved")
        }
    }
}

Broadcast Extension

Create Broadcast Extension

swift
import ReplayKit
import AVFoundation

class SampleHandler: RPBroadcastSampleHandler {
    private var videoWriter: AVAssetWriter?
    private var videoWriterInput: AVAssetWriterInput?

    override func broadcastStarted(withSetupInfo setupInfo: [String: NSObject]!) {
        // Setup broadcast
        let appGroup = "group.com.yourapp.broadcast"
        let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroup)

        let movieURL = containerURL?.appendingPathComponent("broadcast.mp4")

        let writer = try? AVAssetWriter(outputURL: movieURL!, fileType: .mp4)

        let videoSettings: [String: Any] = [
            AVVideoCodecKey: AVVideoCodecType.h264,
            AVVideoWidthKey: 1920,
            AVVideoHeightKey: 1080
        ]

        let writerInput = AVAssetWriterInput(mediaType: AVMediaType.video, outputSettings: videoSettings)

        writer?.add(writerInput)
        writer?.startWriting()

        videoWriter = writer
        videoWriterInput = writerInput
    }

    override func processSampleBuffer(_ sampleBuffer: CMSampleBuffer, with type: RPSampleBufferType) {
        if type == .video {
            guard let writer = videoWriter,
                  let writerInput = videoWriterInput else {
                return
            }

            if writer.status == .unknown {
                writer.startSession(atSourceTime: CMSampleBufferGetPresentationTimeStamp(sampleBuffer))
            }

            if writerInput.isReadyForMoreMediaData {
                writerInput.append(sampleBuffer)
            }
        }
    }

    override func broadcastEnded() {
        videoWriter?.finishWriting {
            print("Broadcast ended")
        }
    }
}

Camera Microphone

Include Camera

swift
extension ScreenRecorderManager {
    func startRecordingWithCamera() {
        recorder = RPScreenRecorder.shared()

        let microphoneEnabled = true

        recorder?.startCapture(
            handler: { [weak self] captureHandler in
                self?.setupWriter()

                captureHandler { sampleBuffer, type, buffer in
                    if type == .video {
                        self?.processSampleBuffer(sampleBuffer)
                    }
                }
            },
            microphone: microphoneEnabled
        )
    }
}

Clip Sharing

Share Clips

swift
extension ScreenRecorderManager {
    func shareRecording(url: URL) {
        let activityViewController = UIActivityViewController(
            activityItems: [url],
            applicationActivities: nil
        )

        if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
           let rootViewController = windowScene.windows.first?.rootViewController {
            rootViewController.present(activityViewController, animated: true)
        }
    }

    func saveToPhotos(url: URL) {
        PHPhotoLibrary.shared().performChanges({
            PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: url)
        }) { success, error in
            if success {
                print("Saved to photos")
            } else {
                print("Error saving: \(error)")
            }
        }
    }
}

App Clip Integration

App Clip Recording

swift
import AppClip

class AppClipRecorder {
    func recordAppClip() {
        let appClipURL = URL(string: "myapp://clip")!

        guard let clip = RPPreviewClip(fileURL: generateClipURL()) else {
            return
        }

        let metadata = RPPreviewClipMetadata(
            appClipURL: appClipURL,
            title: "Try my feature",
            subtitle: "See it in action"
        )

        let previewViewController = RPPreviewViewController(
            previewClip: clip,
            metadata: metadata
        )

        if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
           let rootViewController = windowScene.windows.first?.rootViewController {
            rootViewController.present(previewViewController, animated: true)
        }
    }

    private func generateClipURL() -> URL {
        let tempDir = NSTemporaryDirectory()
        let tempURL = tempDir.appendingPathComponent("clip.mp4")
        return tempURL
    }
}

SwiftUI Integration

Recording Controls

swift
struct RecordingControlsView: View {
    @StateObject private var recorder = ScreenRecorderManager()

    var body: some View {
        VStack(spacing: 20) {
            if recorder.isRecording {
                Button("Stop Recording") {
                    recorder.stopRecording()
                }
                .padding()
                .background(Color.red)
                .foregroundColor(.white)
                .cornerRadius(10)
            } else {
                Button("Start Recording") {
                    recorder.startRecording()
                }
                .padding()
                .background(Color.blue)
                .foregroundColor(.white)
                .cornerRadius(10)
            }
        }
    }
}

Best Practices

  1. 1Permissions: Request proper permissions
  2. 2Storage: Manage storage efficiently
  3. 3Quality: Optimize recording quality
  4. 4Battery: Consider battery impact
  5. 5UI: Provide recording controls
  6. 6Preview: Show recording preview
  7. 7Sharing: Enable easy sharing
  8. 8Testing: Test on real devices

ReplayKit enables powerful screen recording and broadcasting!

Related essays

Next essay
Swift PencilKit: Drawing and Handwriting