Contents

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:

  1. Go to File > Add Package Dependencies
  2. Enter: https://github.com/AlmoutasemNabil/SessionReplaySDK
  3. 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.userInfo

User 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:

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 mask

Manual 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 facade

Changelog

Version 0.2.0

  • User Identification: New identifyUser() and setUserInfo() 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 LiveActivityView and ActivityTimelineView SwiftUI components
  • Debug Logging Control: New debugLogging config 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!

  1. Fork the repository
  2. Create feature branch: git checkout -b feature/amazing
  3. Commit changes: git commit -m 'Add amazing feature'
  4. Push: git push origin feature/amazing
  5. Open Pull Request

Please include tests and update documentation.

Requirements

  • iOS 15.0+
  • Swift 5.9+
  • Xcode 15.0+

Support


πŸ“„ 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