ReplayKit enables your app to record screen content and broadcast to streaming platforms. Learn to implement recording features.
ReplayKit Setup
Add ReplayKit Capability
- 1Go to Project Settings > Signing & Capabilities
- 2Add "ReplayKit" capability
- 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
- 1Permissions: Request proper permissions
- 2Storage: Manage storage efficiently
- 3Quality: Optimize recording quality
- 4Battery: Consider battery impact
- 5UI: Provide recording controls
- 6Preview: Show recording preview
- 7Sharing: Enable easy sharing
- 8Testing: Test on real devices
ReplayKit enables powerful screen recording and broadcasting!