Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/chilly-coats-grow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"posthog-ios": minor
---

feat: support session replay minimum recording duration
11 changes: 10 additions & 1 deletion .swiftlint.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
excluded: # case-sensitive paths to ignore during linting. Takes precedence over `included`
- PostHogExample
- PostHogExampleWithSPM
- PostHogExampleAutocapture
- PostHogExampleExternalSDK
- PostHogExampleMacOS
- PostHogExampleStoryboard
- PostHogExampleTvOS
- PostHogExampleVisionOS
- PostHogExampleWatchOS
- PostHogExampleWidget
- PostHogExampleWidgetAppGroup
- PostHogExampleWithPods
- PostHogExampleWithSPM
- PostHogTests
- PostHog/Utils/ReadWriteLock.swift
- PostHog/Utils/Reachability.swift
Expand Down
20 changes: 20 additions & 0 deletions PostHog.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,9 @@
DA5064412EF5E40300C51DA0 /* ApplicationScreenViewPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA578E992D676AE700B3A56C /* ApplicationScreenViewPublisher.swift */; };
DA5064432EF5E58D00C51DA0 /* PostHogErrorTrackingAutoCaptureIntegration.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA5064422EF5E58D00C51DA0 /* PostHogErrorTrackingAutoCaptureIntegration.swift */; };
DA5064572EF6171900C51DA0 /* SwiftCrashTriggers.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA5064562EF6171900C51DA0 /* SwiftCrashTriggers.swift */; };
DA5175B32F6C4CEA00F0E00A /* PostHogReplayBufferDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA5175B12F6C4CEA00F0E00A /* PostHogReplayBufferDelegate.swift */; };
DA5175B42F6C4CEA00F0E00A /* PostHogReplayBufferQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA5175B22F6C4CEA00F0E00A /* PostHogReplayBufferQueue.swift */; };
DA5175BC2F6C4E1100F0E00A /* PostHogReplayQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA5175BB2F6C4E1100F0E00A /* PostHogReplayQueue.swift */; };
DA52CACB2F24027500305F3D /* PostHogMulticastCallback.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA52CACA2F24027500305F3D /* PostHogMulticastCallback.swift */; };
DA52CAD32F2402E000305F3D /* PostHogMulticastCallbackTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA52CAD22F2402E000305F3D /* PostHogMulticastCallbackTest.swift */; };
DA52CAEF2F24319800305F3D /* PostHogSessionReplayPluginTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA52CAEE2F24319800305F3D /* PostHogSessionReplayPluginTest.swift */; };
Expand All @@ -210,6 +213,7 @@
DA5E52E22E09930600159D20 /* fixture_survey_unknown_type.json in Resources */ = {isa = PBXBuildFile; fileRef = DA5E52E12E09930600159D20 /* fixture_survey_unknown_type.json */; };
DA5E52E32E09930600159D20 /* fixture_survey_unknown_question_type.json in Resources */ = {isa = PBXBuildFile; fileRef = DA5E52E02E09930600159D20 /* fixture_survey_unknown_question_type.json */; };
DA5E52EB2E0996C400159D20 /* PostHogSurveyEnumsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA5E52EA2E0996C400159D20 /* PostHogSurveyEnumsTest.swift */; };
DA641B392F6D873600BAD1E9 /* PostHogReplayQueueTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA641B382F6D873600BAD1E9 /* PostHogReplayQueueTest.swift */; };
DA690C6A2DA54BCC0045FF4E /* PostHogSurveyAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA690C692DA54BC70045FF4E /* PostHogSurveyAppearance.swift */; };
DA690C6C2DA54BD70045FF4E /* PostHogSurveyConditions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA690C6B2DA54BD10045FF4E /* PostHogSurveyConditions.swift */; };
DA690C6E2DA54BEC0045FF4E /* PostHogSurveyQuestion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA690C6D2DA54BE40045FF4E /* PostHogSurveyQuestion.swift */; };
Expand Down Expand Up @@ -384,6 +388,7 @@
DAD4B2F52D79E25100DA503C /* SurveysWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD4B2EF2D79E24D00DA503C /* SurveysWindow.swift */; };
DAD5DD0C2CB6DEF30087387B /* PostHogMaskViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD5DD072CB6DEE70087387B /* PostHogMaskViewModifier.swift */; };
DADE7DFE2F7EA90D00C4650C /* PostHogDeepLinkIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DADE7DFD2F7EA90D00C4650C /* PostHogDeepLinkIntegrationTests.swift */; };
DAEA0FD12F6D5191000E33D8 /* PostHogReplayBufferQueueTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAEA0FD02F6D5191000E33D8 /* PostHogReplayBufferQueueTest.swift */; };
DAF79A2A2D1309C00078A3C9 /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAF79A242D1309BE0078A3C9 /* TestError.swift */; };
DAF95F512D072F04001E82BB /* UIImage+WebP.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAF95F502D072F04001E82BB /* UIImage+WebP.swift */; };
DAF95F612D077C21001E82BB /* PostHogWebPTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAF95F602D077C1C001E82BB /* PostHogWebPTest.swift */; };
Expand Down Expand Up @@ -758,6 +763,9 @@
DA5063D12EF5918200C51DA0 /* PostHogCrashReportProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogCrashReportProcessor.swift; sourceTree = "<group>"; };
DA5064422EF5E58D00C51DA0 /* PostHogErrorTrackingAutoCaptureIntegration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogErrorTrackingAutoCaptureIntegration.swift; sourceTree = "<group>"; };
DA5064562EF6171900C51DA0 /* SwiftCrashTriggers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftCrashTriggers.swift; sourceTree = "<group>"; };
DA5175B12F6C4CEA00F0E00A /* PostHogReplayBufferDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogReplayBufferDelegate.swift; sourceTree = "<group>"; };
DA5175B22F6C4CEA00F0E00A /* PostHogReplayBufferQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogReplayBufferQueue.swift; sourceTree = "<group>"; };
DA5175BB2F6C4E1100F0E00A /* PostHogReplayQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogReplayQueue.swift; sourceTree = "<group>"; };
DA52CACA2F24027500305F3D /* PostHogMulticastCallback.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogMulticastCallback.swift; sourceTree = "<group>"; };
DA52CAD22F2402E000305F3D /* PostHogMulticastCallbackTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogMulticastCallbackTest.swift; sourceTree = "<group>"; };
DA52CAEE2F24319800305F3D /* PostHogSessionReplayPluginTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogSessionReplayPluginTest.swift; sourceTree = "<group>"; };
Expand All @@ -773,6 +781,7 @@
DA5E52E02E09930600159D20 /* fixture_survey_unknown_question_type.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = fixture_survey_unknown_question_type.json; sourceTree = "<group>"; };
DA5E52E12E09930600159D20 /* fixture_survey_unknown_type.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = fixture_survey_unknown_type.json; sourceTree = "<group>"; };
DA5E52EA2E0996C400159D20 /* PostHogSurveyEnumsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogSurveyEnumsTest.swift; sourceTree = "<group>"; };
DA641B382F6D873600BAD1E9 /* PostHogReplayQueueTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogReplayQueueTest.swift; sourceTree = "<group>"; };
DA690C692DA54BC70045FF4E /* PostHogSurveyAppearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogSurveyAppearance.swift; sourceTree = "<group>"; };
DA690C6B2DA54BD10045FF4E /* PostHogSurveyConditions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogSurveyConditions.swift; sourceTree = "<group>"; };
DA690C6D2DA54BE40045FF4E /* PostHogSurveyQuestion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogSurveyQuestion.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -942,6 +951,7 @@
DAD4B2EF2D79E24D00DA503C /* SurveysWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurveysWindow.swift; sourceTree = "<group>"; tabWidth = 5; };
DAD5DD072CB6DEE70087387B /* PostHogMaskViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogMaskViewModifier.swift; sourceTree = "<group>"; };
DADE7DFD2F7EA90D00C4650C /* PostHogDeepLinkIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogDeepLinkIntegrationTests.swift; sourceTree = "<group>"; };
DAEA0FD02F6D5191000E33D8 /* PostHogReplayBufferQueueTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogReplayBufferQueueTest.swift; sourceTree = "<group>"; };
DAF79A242D1309BE0078A3C9 /* TestError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestError.swift; sourceTree = "<group>"; };
DAF7D2DC2D95ACA500016C39 /* PostHogExampleVisionOS.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = PostHogExampleVisionOS.xcodeproj; path = PostHogExampleVisionOS/PostHogExampleVisionOS.xcodeproj; sourceTree = "<group>"; };
DAF95F502D072F04001E82BB /* UIImage+WebP.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+WebP.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1213,6 +1223,8 @@
DA697CAE2EF935490019640F /* PostHogExceptionProcessorTest.swift */,
DA697CAF2EF935490019640F /* PostHogStackTraceProcessorTest.swift */,
DADE7DFD2F7EA90D00C4650C /* PostHogDeepLinkIntegrationTests.swift */,
DA641B382F6D873600BAD1E9 /* PostHogReplayQueueTest.swift */,
DAEA0FD02F6D5191000E33D8 /* PostHogReplayBufferQueueTest.swift */,
);
path = PostHogTests;
sourceTree = "<group>";
Expand Down Expand Up @@ -1350,6 +1362,9 @@
69F23A772BB30991001194F6 /* NetworkSample.swift */,
69F23A792BB309F3001194F6 /* MethodSwizzler.swift */,
6955CB722C517651008EFD8D /* CGSize+Util.swift */,
DA5175BB2F6C4E1100F0E00A /* PostHogReplayQueue.swift */,
DA5175B12F6C4CEA00F0E00A /* PostHogReplayBufferDelegate.swift */,
DA5175B22F6C4CEA00F0E00A /* PostHogReplayBufferQueue.swift */,
);
path = Replay;
sourceTree = "<group>";
Expand Down Expand Up @@ -2219,6 +2234,8 @@
DAB9E23D2DE8901D00E62DB9 /* URLSessionExtension.swift in Sources */,
DAB9E23E2DE8901D00E62DB9 /* URLSessionSwizzler.swift in Sources */,
DAB9E23F2DE8901D00E62DB9 /* URLSessionInterceptor.swift in Sources */,
DA5175B32F6C4CEA00F0E00A /* PostHogReplayBufferDelegate.swift in Sources */,
DA5175B42F6C4CEA00F0E00A /* PostHogReplayBufferQueue.swift in Sources */,
69F23A782BB30991001194F6 /* NetworkSample.swift in Sources */,
DA30AE692D3EFB4F00465A64 /* Optional+Util.swift in Sources */,
DA9AE8D52D841994002F1B44 /* Survey+Util.swift in Sources */,
Expand Down Expand Up @@ -2247,6 +2264,7 @@
DAC699EC2CCA73E5000D1D6B /* ForwardingPickerViewDelegate.swift in Sources */,
DA26419C2CC0499300CB427B /* PostHogAutocaptureEventTracker.swift in Sources */,
DA5B85882CD21CBB00686389 /* AutocaptureEventProcessing.swift in Sources */,
DA5175BC2F6C4E1100F0E00A /* PostHogReplayQueue.swift in Sources */,
69ED1A5C2C7F15F300FE7A91 /* PostHogSessionManager.swift in Sources */,
69F518142BAC7F4300F52C14 /* Date+Util.swift in Sources */,
DA86D0E32E02D78C00CF3065 /* PostHogSurveysDefaultDelegate.swift in Sources */,
Expand Down Expand Up @@ -2444,6 +2462,7 @@
DA578E9E2D6858BA00B3A56C /* PostHogScreenViewIntegrationTest.swift in Sources */,
3A62646A29C9E385007E8C07 /* MockPostHogServer.swift in Sources */,
690FF0BB2AEF8B8200A0B06B /* PostHogContextTest.swift in Sources */,
DAEA0FD12F6D5191000E33D8 /* PostHogReplayBufferQueueTest.swift in Sources */,
DA52CAD32F2402E000305F3D /* PostHogMulticastCallbackTest.swift in Sources */,
690FF0E32AEFD12900A0B06B /* PostHogConfigTest.swift in Sources */,
DAF79A2A2D1309C00078A3C9 /* TestError.swift in Sources */,
Expand Down Expand Up @@ -2471,6 +2490,7 @@
DA697CB32EF935490019640F /* PostHogStackTraceProcessorTest.swift in Sources */,
DA697CB42EF935490019640F /* PostHogCrashReportProcessorTest.swift in Sources */,
DA697CB52EF935490019640F /* PostHogExceptionProcessorTest.swift in Sources */,
DA641B392F6D873600BAD1E9 /* PostHogReplayQueueTest.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
14 changes: 13 additions & 1 deletion PostHog/PostHogFileBackedQueue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ class PostHogFileBackedQueue {
}

do {
let sortedItems = try FileManager.default.contentsOfDirectory(at: queue, sortedBy: .contentModificationDateKey)
// when copying over buffered snapshots, content modification date will change, so we work off creation date instead.
let sortedItems = try FileManager.default.contentsOfDirectory(at: queue, sortedBy: .creationDateKey)
itemsLock.withLock { items = sortedItems }
} catch {
hedgeLog("Failed to load files for queue \(error)")
Expand Down Expand Up @@ -85,6 +86,17 @@ class PostHogFileBackedQueue {
setup(oldQueues: [])
}

/// Reloads items from disk and sorts by creation date.
/// Use after externally adding files to the queue directory.
func reloadFromDisk() {
do {
let sortedItems = try FileManager.default.contentsOfDirectory(at: queue, sortedBy: .creationDateKey)
itemsLock.withLock { items = sortedItems }
} catch {
hedgeLog("Failed to reload files for queue \(error)")
}
}

private func loadFiles(_ count: Int) -> [Data] {
var results = [Data]()

Expand Down
4 changes: 2 additions & 2 deletions PostHog/PostHogQueue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class PostHogQueue {
fileQueue.depth
}

private let fileQueue: PostHogFileBackedQueue
let fileQueue: PostHogFileBackedQueue

#if !os(watchOS)
init(_ config: PostHogConfig, _ storage: PostHogStorage, _ api: PostHogApi, _ endpoint: PostHogApiEndpoint, _ reachability: Reachability?) {
Expand Down Expand Up @@ -198,7 +198,7 @@ class PostHogQueue {
}
}

private func flushIfOverThreshold() {
func flushIfOverThreshold() {
if fileQueue.depth >= config.flushAt {
flush()
}
Expand Down
31 changes: 31 additions & 0 deletions PostHog/PostHogRemoteConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class PostHogRemoteConfig {
private let sessionReplayLock = NSLock()
private var sessionReplayFlagActive = false
private var recordingSampleRate: Double?
private var recordingMinimumDuration: TimeInterval?

private let errorTrackingLock = NSLock()
private var autoCaptureExceptions = false
Expand Down Expand Up @@ -234,6 +235,7 @@ class PostHogRemoteConfig {
sessionReplayFlagActive = isRecordingActive(featureFlags ?? [:], sessionReplay)
#if os(iOS)
recordingSampleRate = parseSampleRate(sessionReplay["sampleRate"])
recordingMinimumDuration = parseMinimumDuration(sessionReplay["minimumDurationMilliseconds"])
#endif
}
}
Expand Down Expand Up @@ -434,6 +436,7 @@ class PostHogRemoteConfig {
}
sessionReplayLock.withLock {
recordingSampleRate = parseSampleRate(sessionRecording["sampleRate"])
recordingMinimumDuration = parseMinimumDuration(sessionRecording["minimumDurationMilliseconds"])
sessionReplayFlagActive = isRecordingActive(featureFlags, sessionRecording)
}
storage.setDictionary(forKey: .sessionReplay, contents: sessionRecording)
Expand Down Expand Up @@ -467,6 +470,34 @@ class PostHogRemoteConfig {
func getRecordingSampleRate() -> Double? {
sessionReplayLock.withLock { recordingSampleRate }
}

/// Parses and validates a minimum duration value which may come as a Number (from the API JSON)
/// or from cached storage. Returns `nil` if the value is absent, unparseable, or negative.
/// The value is expected to be in milliseconds.
private func parseMinimumDuration(_ raw: Any?) -> TimeInterval? {
let milliseconds: Double?
if let number = raw as? Double {
milliseconds = number
} else if let number = raw as? NSNumber {
milliseconds = number.doubleValue
} else if let number = raw as? Int {
milliseconds = Double(number)
} else {
return nil
}

guard let milliseconds, milliseconds >= 0 else {
if let milliseconds {
hedgeLog("Remote config minimumDurationMilliseconds must be non-negative, got \(milliseconds). Ignoring.")
}
return nil
}
return milliseconds / 1_000.0
}

func getRecordingMinimumDuration() -> TimeInterval? {
sessionReplayLock.withLock { recordingMinimumDuration }
}
#endif

private func processErrorTrackingConfig(_ data: [String: Any]?) {
Expand Down
13 changes: 8 additions & 5 deletions PostHog/PostHogSDK.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ let maxRetryDelay = 30.0
private var cachedPersonPropertiesHash: String?

private var queue: PostHogQueue?
private var replayQueue: PostHogQueue?
private(set) var replayQueue: PostHogReplayQueue?
private(set) var storage: PostHogStorage?
#if !os(watchOS)
private var reachability: Reachability?
Expand Down Expand Up @@ -137,10 +137,10 @@ let maxRetryDelay = 30.0

#if !os(watchOS)
queue = PostHogQueue(config, theStorage, api, .batch, reachability)
replayQueue = PostHogQueue(config, theStorage, api, .snapshot, reachability)
replayQueue = PostHogReplayQueue(config, theStorage, api, reachability)
#else
queue = PostHogQueue(config, theStorage, api, .batch)
replayQueue = PostHogQueue(config, theStorage, api, .snapshot)
replayQueue = PostHogReplayQueue(config, theStorage, api)
#endif

queue?.start(disableReachabilityForTesting: config.disableReachabilityForTesting,
Expand Down Expand Up @@ -970,8 +970,11 @@ let maxRetryDelay = 30.0
}

// Session Replay has its own queue
if let targetQueue = isSnapshotEvent ? replayQueue : queue {
queueEvent(posthogEvent, queue: targetQueue)
if isSnapshotEvent {
replayQueue?.add(posthogEvent)
onEventCaptured.invoke(posthogEvent)
} else {
queueEvent(posthogEvent, queue: queue)
}
}

Expand Down
1 change: 1 addition & 0 deletions PostHog/PostHogStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ class PostHogStorage {
case oldQueueFolder = "posthog.queueFolder" // queue from 3.0.0 - 3.48.1
case oldQueuePlist = "posthog.queue.plist" // queue from pre-3.0.0
case replayQeueue = "posthog.replayFolder"
case replayBufferQueue = "posthog.replayBufferFolder"
case enabledFeatureFlags = "posthog.enabledFeatureFlags"
case enabledFeatureFlagPayloads = "posthog.enabledFeatureFlagPayloads"
case flags = "posthog.flags"
Expand Down
22 changes: 22 additions & 0 deletions PostHog/Replay/PostHogReplayBufferDelegate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import Foundation

/// Delegate protocol for controlling session replay buffering behavior
// This will be usually the `PostHogReplayIntegation`
///
/// The replay queue is passive: it checks `isBuffering` on every `add()` and `flush()`,
/// and notifies the delegate after buffering a snapshot. The delegate (replay integration)
/// has full control over:
/// 1. Whether we are buffering (`isBuffering`)
/// 2. When to flush buffer to real queue (calls `replayQueue.migrateBufferToQueue()`)
/// 3. When to clear the buffer (calls `replayQueue.clearBuffer()`)
protocol PostHogReplayBufferDelegate: AnyObject {
/// Whether the replay queue should buffer snapshots instead of sending directly.
/// Checked on every `queue.add()` and `queue.flush()`.
var isBuffering: Bool { get }

/// Called after a snapshot was added to the buffer.
/// The delegate should check threshold conditions and call
/// `replayQueue.migrateBufferToQueue()` when the minimum duration has been met.
/// This will copy temp buffer queue to the real replay queue
func replayQueueDidBufferSnapshot(_ replayQueue: PostHogReplayQueue)
}
Loading
Loading