AlmoutasemNabil/SessionReplaySDK
πΉ Lightweight iOS SDK for recording user sessions with video, touch events, console logs & network requests. Privacy-first with automatic PII masking. Pure Swift, zero dependencies.
Features
| Feature | Description | |---------|-------------| | Video Recording | H.264 encoded screen capture with configurable quality and frame rate | | Touch Visualization | Record and overlay touch events with visual indicators | | Console Log Capture | Automatically intercept print(), NSLog(), and stderr | | Network Tracking | Monitor all HTTP/HTTPS requests (works with Alamofire SSL pinning) | | User Identification | Attach user info (userId, email, custom data) to sessions | | Auto Start/Stop | Configurable automatic session lifecycle management | | Crash Recovery | Periodic checkpoints to recover sessions after crashes | | App Groups | Share session data between app and extensions | | Screen Transitions | Track navigation flow between screens | | Synchronized Timeline | All events timestamped for video sync playback | | Local Storage | Save sessions locally with JSON metadata | | Cloud Upload | Multipart form data upload to your backend | | Privacy Controls | Redact headers, exclude URLs, sanitize logs | | SwiftUI & UIKit | Full support with view modifiers and components |
Installation
Swift Package Manager
Xcode:
- Go to File > Add Package Dependencies
- Enter:
https://github.com/AlmoutasemNabil/SessionReplaySDK - Select version and add to your target
Package.swift:
dependencies: [
.package(url: "https://github.com/AlmoutasemNabil/SessionReplaySDK", from: "0.2.0")
]Quick Start
1. Configure on App Launch
import SessionReplaySDK
@main
struct MyApp: App {
init() {
var config = SessionReplayConfig()
config.captureFrameRate = 10
config.jpegCompressionQuality = 0.7
config.autoStartOnLaunch = false // Manual control
config.enableCrashRecovery = true // Save on crash
config.debugLogging = false // Disable in production
SessionReplaySDK.configure(
videoConfig: config,
logConfig: SessionLoggerConfig()
)
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}2. Logs-Only Mode (No Video)
For lightweight logging without video recording:
var config = SessionReplayConfig()
config.enableVideoRecording = false // Disable video, capture logs only
SessionReplaySDK.configure(
videoConfig: config,
logConfig: SessionLoggerConfig()
)
// Start/stop works the same way
SessionReplaySDK.start()
// ... session captures console logs and network requests only
SessionReplaySDK.stop()3. Identify Users
// After user logs in
SessionReplaySDK.identifyUser(
userId: "user_123",
email: "user@example.com",
name: "John Doe",
additionalInfo: ["plan": "premium", "branchId": "branch_456"]
)
// Or set custom info directly
SessionReplaySDK.setUserInfo([
"userId": "123",
"email": "user@example.com",
"customField": "value"
])3. Start/Stop Recording
// Start recording
SessionReplaySDK.startSession()
// Stop recording
SessionReplaySDK.stopSession()
// Check status
if SessionReplaySDK.isRecording {
print("Recording in progress...")
}4. Track Screens (SwiftUI)
struct HomeView: View {
var body: some View {
NavigationView {
// Your content
}
.trackScreen("HomeScreen")
}
}5. Custom Logging
SessionReplaySDK.debug("Debug: User opened settings")
SessionReplaySDK.info("Info: Purchase completed")
SessionReplaySDK.warning("Warning: Low storage")
SessionReplaySDK.error("Error: Network timeout")6. Upload Sessions
// Configure endpoint
SessionReplaySDK.configureUpload(
baseURL: URL(string: "https://api.yourserver.com/sessions")!,
apiKey: "your-api-key"
)
// Upload specific session
SessionReplaySDK.uploadSession(sessionId: "abc-123") { result in
switch result {
case .success(let response):
print("Uploaded: \(response.sessionId)")
case .failure(let error):
print("Failed: \(error)")
}
}
// Upload all sessions
SessionReplaySDK.uploadAllSessions { results in
let successful = results.filter { $0.1.isSuccess }.count
print("Uploaded \(successful)/\(results.count) sessions")
}Configuration
Video (SessionReplayConfig)
| Option | Default | Description | |--------|---------|-------------| | enableVideoRecording | true | Enable video recording (false for logs-only mode) | | captureFrameRate | 1 | Frames per second (1-30) | | jpegCompressionQuality | 0.3 | JPEG quality (0.0-1.0) | | captureScale | 1.0 | Resolution scale (0.25-1.0) | | videoBitrate | 75,000 | H.264 bitrate in bps | | captureTouches | true | Record touch events | | showTouchIndicators | true | Draw touch dots on video | | maskSensitiveViews | true | Mask marked sensitive views | | sensitiveViewMaskColor | .gray | Color for masked areas | | autoMaskTextFields | true | Auto-mask text inputs | | autoMaskSecureTextFields | true | Auto-mask password fields | | autoMaskViewClasses | ` | Custom classes to auto-mask | | maxStorageSize | 50MB | Max local storage | | storageDirectory` | Documents | Custom storage location |
Auto Start/Stop (SessionReplayConfig)
| Option | Default | Description | |--------|---------|-------------| | autoStartOnLaunch | false | Start recording on app launch | | autoStopOnBackground | true | Stop when app enters background | | autoStopOnTerminate | true | Emergency save on terminate | | enableCrashRecovery | true | Save checkpoints periodically | | crashRecoveryInterval | 5.0 | Seconds between checkpoints | | debugLogging | true | Print debug logs to console |
Logging (SessionLoggerConfig)
| Option | Default | Description | |--------|---------|-------------| | captureConsoleLogs | true | Capture stdout/stderr | | captureNetworkRequests | true | Intercept URLSession | | minimumLogLevel | .debug | Minimum capture level | | maxLogMessageLength | 2000 | Truncate long messages | | maxBodySize | 100KB | Max request/response body | | redactedHeaders | [auth, cookie...] | Headers to redact | | excludedURLPatterns | ` | URL regex patterns to skip | | logSanitizer | nil | Custom sanitization closure | | excludeSDKLogs | true` | Exclude SDK internal logs from session data |
Upload (SessionUploadConfig)
| Option | Default | Description | |--------|---------|-------------| | baseURL | Required | Your upload endpoint | | apiKey | nil | Bearer token auth | | additionalHeaders | [:] | Custom headers | | maxRetries | 3 | Retry on failure | | timeoutInterval | 120s | Request timeout | | deleteAfterUpload | false | Remove local files |
App Group Support
Share session data between your main app and extensions:
// Configure with App Group
SessionReplaySDK.configureWithAppGroup(
"group.com.yourcompany.yourapp",
videoConfig: config,
logConfig: logConfig
)
// Or manually set storage directory
var config = SessionReplayConfig()
if let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.yourapp") {
config.storageDirectory = containerURL.appendingPathComponent("SessionReplays", isDirectory: true)
}
SessionReplaySDK.configure(videoConfig: config)User Identification
Attach user info to sessions for easier debugging:
// Simple identification
SessionReplaySDK.identifyUser(
userId: "user_123",
email: "user@example.com",
name: "John Doe"
)
// With additional custom data
SessionReplaySDK.identifyUser(
userId: "cashier_456",
email: "cashier@foodics.com",
additionalInfo: [
"branchId": "branch_123",
"role": "cashier",
"shiftId": "shift_789"
]
)
// Set individual values
SessionReplaySDK.setUserInfo(key: "subscriptionTier", value: "premium")
// Clear on logout
SessionReplaySDK.clearUserInfo()
// Access current info
let currentUser = SessionReplaySDK.userInfoUser info is saved in both metadata.userInfo and root userInfo in the session JSON.
Crash Recovery
The SDK automatically saves session checkpoints:
var config = SessionReplayConfig()
config.enableCrashRecovery = true // Enable checkpoints
config.crashRecoveryInterval = 3.0 // Save every 3 seconds
SessionReplaySDK.configure(videoConfig: config)
// Check for incomplete sessions after app restart
let incompleteSessions = SessionReplaySDK.recoverIncompleteSessions()
for sessionId in incompleteSessions {
print("Found incomplete session: \(sessionId)")
// Video segments may be available even if session didn't complete
}Session Data Format
{
"sessionId": "550e8400-e29b-41d4-a716-446655440000",
"startTime": "2025-01-27T10:30:00Z",
"endTime": "2025-01-27T10:35:00Z",
"durationMs": 300000,
"frameCount": 3000,
"videoSegments": ["session_segment0.mp4"],
"userInfo": {
"userId": "user_123",
"email": "user@example.com",
"branchId": "branch_456"
},
"touches": [
{"timestamp": 1500, "location": {"x": 200, "y": 400}, "phase": 0}
],
"logs": [
{"timestamp": 2000, "level": "info", "message": "Button tapped", "source": "stdout"}
],
"networkRequests": [
{"timestamp": 3000, "method": "GET", "url": "https://api.example.com/data", "statusCode": 200, "duration": 150}
],
"screenTransitions": [
{"timestamp": 0, "screenName": "HomeScreen"}
],
"metadata": {
"appVersion": "1.0.0",
"osVersion": "17.0",
"deviceModel": "iPhone15,2",
"locale": "en_US",
"userInfo": {
"userId": "user_123",
"email": "user@example.com"
}
}
}Built-in Components
SwiftUI
// Recording controls
RecordingControlView()
// Sessions list with upload
SessionsListView()
// Activity timeline (live during recording)
LiveActivityView()
// Activity timeline (for completed sessions)
ActivityTimelineView(session: selectedSession)
// Session player
SessionReplayViewer(session: selectedSession)
// Screen tracking modifier
.trackScreen("ScreenName")
// Touch indicators overlay
.withTouchIndicators()
// Mark sensitive content
.sensitiveContent()UIKit
// Base view controller with tracking
class MyVC: SessionReplayViewController {
override var screenName: String { "MyScreen" }
}
// Debug view controller
let debugVC = SessionReplayDebugViewController()
present(debugVC, animated: true)
// Mark sensitive views
passwordField.markAsSensitive()Network Capture
The SDK captures all URLSession-based network requests, including:
- Direct URLSession usage
- Alamofire (including custom sessions with SSL pinning)
- FNetwork and other URLSession-based libraries
No additional configuration neededβnetwork capture works automatically.
Upload API
The SDK sends multipart form data:
| Field | Type | Description | |-------|------|-------------| | sessionId | String | Session identifier | | video | File | MP4 video file | | metadata | File | JSON metadata file |
Expected Response:
{
"sessionId": "...",
"videoURL": "https://...",
"metadataURL": "https://...",
"message": "Success"
}Testing Upload:
- Use webhook.site for instant testing
- Use requestbin.com as alternative
- Or
https://postman-echo.com/postfor echo testing
Privacy & Security
Sensitive Data Masking
The SDK automatically masks sensitive content in screen recordings:
Automatic Masking (enabled by default):
- Secure text fields (password inputs)
- Regular text fields and text views
- Views marked with
markAsSensitive()or.sensitiveContent()
Configuration Options:
var config = SessionReplayConfig()
config.maskSensitiveViews = true // Enable/disable masking
config.sensitiveViewMaskColor = .gray // Mask color
config.autoMaskTextFields = true // Auto-mask UITextField/UITextView
config.autoMaskSecureTextFields = true // Auto-mask password fields
config.autoMaskViewClasses = ["CreditCardView"] // Custom classes to maskManual Masking - UIKit:
// Mark any UIView as sensitive
passwordField.markAsSensitive()
creditCardView.markAsSensitive()
// Check if view is marked
if myView.isSensitive { ... }Manual Masking - SwiftUI:
// Mark any SwiftUI view as sensitive
SecretDataView()
.sensitiveContent()
// With custom mask color
PaymentForm()
.sensitiveContent(maskColor: .black)Additional Privacy Features
- Header Redaction: Authorization, Cookie, API keys auto-redacted from network logs
- URL Exclusion: Regex patterns to exclude specific endpoints
- Log Sanitization: Custom closure for PII removal
- Local-First: All data stored locally, upload is opt-in
- No Dependencies: Pure Swift, no third-party tracking code
Architecture
SessionReplaySDK/
βββ Core/
β βββ Models.swift # Data structures
β βββ SessionReplayManager.swift # Main controller
β βββ VideoWriter.swift # H.264 encoding
βββ Logging/
β βββ SessionLogger.swift # Console capture
β βββ NetworkInterceptor.swift # URL monitoring
βββ Upload/
β βββ SessionUploader.swift # Cloud upload
βββ Integration/
β βββ SwiftUIIntegration.swift # SwiftUI support
β βββ UIKitIntegration.swift # UIKit support
βββ SessionReplaySDK.swift # Public facadeChangelog
Version 0.2.0
- User Identification: New
identifyUser()andsetUserInfo()APIs to attach user data to sessions - App Group Support: Configure custom storage directory for sharing data between app and extensions
- Auto Start/Stop: New config options for automatic session lifecycle management
- Crash Recovery: Periodic checkpoints to recover sessions after crashes
- Network Capture Improvements: Works with all URLSession-based networking including Alamofire with SSL pinning
- Activity Timeline Views: New
LiveActivityViewandActivityTimelineViewSwiftUI components - Debug Logging Control: New
debugLoggingconfig to reduce console noise in production - Removed Verbose Touch Logging: Touch began/ended events no longer spam the console
Version 0.1.0
- Initial release
- Video recording with H.264 encoding
- Touch event capture and visualization
- Console log interception
- Network request monitoring
- SwiftUI and UIKit integration
- Cloud upload support
Contributing
We welcome contributions!
- Fork the repository
- Create feature branch:
git checkout -b feature/amazing - Commit changes:
git commit -m 'Add amazing feature' - Push:
git push origin feature/amazing - Open Pull Request
Please include tests and update documentation.
Requirements
- iOS 15.0+
- Swift 5.9+
- Xcode 15.0+
Support
- Issues: GitHub Issues
- Discussions: GitHub Discussions
π License
This project is licensed under the MIT License - see the LICENSE file for details.
<div align="center">
Made with β€οΈ by AlmoutasemNabil
Β© 2026 AlmoutasemNabil. All rights reserved.
β Star this repo if you find it helpful!
</div>
Package Metadata
Repository: AlmoutasemNabil/SessionReplaySDK
Stars: 8
Forks: 0
Open issues: 0
Default branch: main
Primary language: swift
License: MIT
Topics: debugging, developer-tools, ios, mobile-analytics, privacy, screen-recording, session-replay, swift, swift-package-manager, uikit
README: README.md