Skip to content

Commit

Permalink
feat: manual start/stop session recording (#276)
Browse files Browse the repository at this point in the history
* chore: add swiftformat config

* fix: avoid registering for notifications multiple times

* feat: add session replay automatic start config

* fix: capture timestamp along with snapshot events

* feat: add start/stop session recording and refactor session manager

* fix(ci): use Xcode 16 for unit testing

* fix(ci): switch to macos-14 for tests

* fix(ci): tests

* fix: remove start mode from session replay config

* fix: use timestamp at time of checking as the new session timestamp

* feat: capture session id on network request start

* fix: get session id only after snapshot is generated

* fix: don't process $snapshot if session id is missing

* fix: remove todo

* chore: update CHANGELOG

* fix(ci): use latest-stable Xcode version

* feat: additional checks on react native

* fix: lint

* fix: remove didFinishLaunching notification

* fix: tests

* chore(ci): build examples on latest macos
ioannisj authored Jan 8, 2025
1 parent 83ed3b4 commit bd28e78
Showing 31 changed files with 1,330 additions and 228 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build-examples.yml
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ on:
- "**/*.md"
jobs:
build:
runs-on: macos-13
runs-on: macos-14
steps:
- uses: actions/checkout@v4
- uses: maxim-lobanov/setup-xcode@v1
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ on:
- "**/*.md"
jobs:
build:
runs-on: macos-13
runs-on: macos-14
steps:
- uses: actions/checkout@v4
- uses: maxim-lobanov/setup-xcode@v1
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ on:
- "**/*.md"
jobs:
test:
runs-on: macos-13
runs-on: macos-14
steps:
- uses: actions/checkout@v4
- uses: maxim-lobanov/setup-xcode@v1
96 changes: 96 additions & 0 deletions .swiftformat
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
--acronyms ID,URL,UUID
--allman false
--anonymousforeach convert
--assetliterals visual-width
--asynccapturing
--beforemarks
--binarygrouping none
--callsiteparen default
--categorymark "MARK: %c"
--classthreshold 0
--closingparen balanced
--closurevoid remove
--commas always
--complexattrs preserve
--computedvarattrs preserve
--condassignment after-property
--conflictmarkers reject
--dateformat system
--decimalgrouping ignore
--doccomments before-declarations
--elseposition same-line
--emptybraces no-space
--enumnamespaces always
--enumthreshold 0
--exponentcase lowercase
--exponentgrouping disabled
--extensionacl on-extension
--extensionlength 0
--extensionmark "MARK: - %t + %c"
--fractiongrouping disabled
--fragment false
--funcattributes preserve
--generictypes
--groupedextension "MARK: %c"
--guardelse auto
--header ignore
--hexgrouping 4,8
--hexliteralcase uppercase
--ifdef indent
--importgrouping alpha
--indent 4
--indentcase false
--indentstrings false
--initcodernil false
--lifecycle
--lineaftermarks true
--linebreaks lf
--markcategories true
--markextensions always
--marktypes always
--maxwidth none
--modifierorder
--nevertrailing
--nilinit remove
--noncomplexattrs
--nospaceoperators
--nowrapoperators
--octalgrouping none
--onelineforeach ignore
--operatorfunc spaced
--organizationmode visibility
--organizetypes actor,class,enum,struct
--patternlet hoist
--ranges spaced
--redundanttype infer-locals-only
--self remove
--selfrequired
--semicolons never
--shortoptionals except-properties
--smarttabs enabled
--someany true
--storedvarattrs preserve
--stripunusedargs always
--structthreshold 0
--tabwidth unspecified
--throwcapturing
--timezone system
--trailingclosures
--trimwhitespace always
--typeattributes preserve
--typeblanklines remove
--typedelimiter space-after
--typemark "MARK: - %t"
--voidtype void
--wraparguments preserve
--wrapcollections preserve
--wrapconditions preserve
--wrapeffects preserve
--wrapenumcases always
--wrapparameters default
--wrapreturntype preserve
--wrapternary default
--wraptypealiases preserve
--xcodeindentation disabled
--yodaswap always
--hexgrouping ignore
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## Next

- feat: ability to manually start and stop session recordings ([#276](https://github.com/PostHog/posthog-ios/pull/276))
- feat: change screenshot encoding format from JPEG to WebP ([#273](https://github.com/PostHog/posthog-ios/pull/273))

## 3.18.0 - 2024-12-27
20 changes: 20 additions & 0 deletions PostHog.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
@@ -140,6 +140,9 @@
DA1044C92D0B2CAC00C4ACF3 /* huffman_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = DA1044C82D0B2CAC00C4ACF3 /* huffman_utils.h */; settings = {ATTRIBUTES = (Public, ); }; };
DA1044D42D0B34F200C4ACF3 /* PostHog.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AC745B5296D6FE60025C109 /* PostHog.framework */; };
DA1044D52D0B34F200C4ACF3 /* PostHog.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3AC745B5296D6FE60025C109 /* PostHog.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
DA1D295E2D10B7B2003A31DA /* ApplicationLifecyclePublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA1D29582D10B7A6003A31DA /* ApplicationLifecyclePublisher.swift */; };
DA1D29602D10C810003A31DA /* PostHogSessionManagerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA1D295F2D10C80D003A31DA /* PostHogSessionManagerTest.swift */; };
DA1D29622D115E17003A31DA /* DI.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA1D29612D115E13003A31DA /* DI.swift */; };
DA26419C2CC0499300CB427B /* PostHogAutocaptureEventTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA26419A2CC0499300CB427B /* PostHogAutocaptureEventTracker.swift */; };
DA4AF61F2D1195D20053EA38 /* PostHog.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AC745B5296D6FE60025C109 /* PostHog.framework */; };
DA4AF6202D1195D20053EA38 /* PostHog.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3AC745B5296D6FE60025C109 /* PostHog.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
@@ -274,6 +277,9 @@
DAD5DD0C2CB6DEF30087387B /* PostHogMaskViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD5DD072CB6DEE70087387B /* PostHogMaskViewModifier.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 */; };
DAD76A212D006AEE003E1A43 /* UIView+PostHogLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD76A1B2D006AE8003E1A43 /* UIView+PostHogLabel.swift */; };
DAD76A242D006C15003E1A43 /* View+PostHogLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD76A232D006C0B003E1A43 /* View+PostHogLabel.swift */; };
DAF79A2A2D1309C00078A3C9 /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAF79A242D1309BE0078A3C9 /* TestError.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
@@ -578,6 +584,9 @@
DA1044C42D0B2C2700C4ACF3 /* vp8li_dec.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = vp8li_dec.h; sourceTree = "<group>"; };
DA1044C62D0B2C5900C4ACF3 /* webpi_dec.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = webpi_dec.h; sourceTree = "<group>"; };
DA1044C82D0B2CAC00C4ACF3 /* huffman_utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = huffman_utils.h; sourceTree = "<group>"; };
DA1D29582D10B7A6003A31DA /* ApplicationLifecyclePublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationLifecyclePublisher.swift; sourceTree = "<group>"; };
DA1D295F2D10C80D003A31DA /* PostHogSessionManagerTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogSessionManagerTest.swift; sourceTree = "<group>"; };
DA1D29612D115E13003A31DA /* DI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DI.swift; sourceTree = "<group>"; };
DA26419A2CC0499300CB427B /* PostHogAutocaptureEventTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogAutocaptureEventTracker.swift; sourceTree = "<group>"; };
DA5AA7132CE245CD004EFB99 /* UIApplication+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIApplication+.swift"; sourceTree = "<group>"; };
DA5B85872CD21CBB00686389 /* AutocaptureEventProcessing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutocaptureEventProcessing.swift; sourceTree = "<group>"; };
@@ -704,6 +713,9 @@
DAD5DD072CB6DEE70087387B /* PostHogMaskViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogMaskViewModifier.swift; sourceTree = "<group>"; };
DAF95F502D072F04001E82BB /* UIImage+WebP.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+WebP.swift"; sourceTree = "<group>"; };
DAF95F602D077C1C001E82BB /* PostHogWebPTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogWebPTest.swift; sourceTree = "<group>"; };
DAD76A1B2D006AE8003E1A43 /* UIView+PostHogLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+PostHogLabel.swift"; sourceTree = "<group>"; };
DAD76A232D006C0B003E1A43 /* View+PostHogLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+PostHogLabel.swift"; sourceTree = "<group>"; };
DAF79A242D1309BE0078A3C9 /* TestError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestError.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
@@ -780,6 +792,7 @@
3A62646829C9E37A007E8C07 /* TestUtils */ = {
isa = PBXGroup;
children = (
DAF79A242D1309BE0078A3C9 /* TestError.swift */,
3A62646929C9E385007E8C07 /* MockPostHogServer.swift */,
3A62647429CB0168007E8C07 /* TestPostHog.swift */,
3A580B4229E489D000C5C6F3 /* URLSession+body.swift */,
@@ -813,6 +826,7 @@
3AA4C09B2988315D006C4731 /* Utils */ = {
isa = PBXGroup;
children = (
DA1D29582D10B7A6003A31DA /* ApplicationLifecyclePublisher.swift */,
DA0CA6F02CFF6B6300AF9500 /* UIWindow+.swift */,
DA5AA7132CE245CD004EFB99 /* UIApplication+.swift */,
3AE3FB422992985A00AFFC18 /* Reachability.swift */,
@@ -871,6 +885,7 @@
children = (
DAB06F9C2D09A744005B1C9B /* PostHog.modulemap */,
3AC745B8296D6FE60025C109 /* PostHog.h */,
DA1D29612D115E13003A31DA /* DI.swift */,
DA26419B2CC0499300CB427B /* Autocapture */,
69EE82B82BA9C4DA00EB9542 /* Replay */,
69BA38E62B893F2200AA69D6 /* Resources */,
@@ -904,6 +919,7 @@
isa = PBXGroup;
children = (
DAF95F5B2D077BCC001E82BB /* Resources */,
DA1D295F2D10C80D003A31DA /* PostHogSessionManagerTest.swift */,
3A62646829C9E37A007E8C07 /* TestUtils */,
3AE3FB4A2993A68500AFFC18 /* PostHogStorageTest.swift */,
3A62647029CAF67B007E8C07 /* PostHogStorageManagerTest.swift */,
@@ -1745,6 +1761,7 @@
69F23A762BB308AE001194F6 /* URLSessionInterceptor.swift in Sources */,
690FF0BF2AEFA97F00A0B06B /* FileUtils.swift in Sources */,
69261D252AD9787A00232EC7 /* PostHogExtensions.swift in Sources */,
DA1D295E2D10B7B2003A31DA /* ApplicationLifecyclePublisher.swift in Sources */,
3AE3FB4E2993D1D600AFFC18 /* PostHogStorageManager.swift in Sources */,
3AE3FB49299391DF00AFFC18 /* PostHogStorage.swift in Sources */,
69261D232AD9784200232EC7 /* PostHogVersion.swift in Sources */,
@@ -1873,6 +1890,7 @@
DA5AA7192CE245D2004EFB99 /* UIApplication+.swift in Sources */,
DA1044902D0B1D4300C4ACF3 /* AssociatedKeys.swift in Sources */,
69EE82CE2BAAC76000EB9542 /* ViewTreeSnapshotStatus.swift in Sources */,
DA1D29622D115E17003A31DA /* DI.swift in Sources */,
69ED1AD42C90A0F100FE7A91 /* URLSessionExtension.swift in Sources */,
69ED1A9F2C8F451B00FE7A91 /* PostHogPersonProfiles.swift in Sources */,
69EE82BA2BA9C50400EB9542 /* PostHogReplayIntegration.swift in Sources */,
@@ -1903,13 +1921,15 @@
3A62646A29C9E385007E8C07 /* MockPostHogServer.swift in Sources */,
690FF0BB2AEF8B8200A0B06B /* PostHogContextTest.swift in Sources */,
690FF0E32AEFD12900A0B06B /* PostHogConfigTest.swift in Sources */,
DAF79A2A2D1309C00078A3C9 /* TestError.swift in Sources */,
3A62647129CAF67B007E8C07 /* PostHogStorageManagerTest.swift in Sources */,
693E977D2C6257F9004B1030 /* ExampleSanitizer.swift in Sources */,
690FF0DF2AEFBC5700A0B06B /* PostHogLegacyQueueTest.swift in Sources */,
690FF0BD2AEF93F400A0B06B /* PostHogFeatureFlagsTest.swift in Sources */,
690FF0E92AEFD3BD00A0B06B /* PostHogQueueTest.swift in Sources */,
3AE3FB4B2993A68500AFFC18 /* PostHogStorageTest.swift in Sources */,
3A580B4329E489D000C5C6F3 /* URLSession+body.swift in Sources */,
DA1D29602D10C810003A31DA /* PostHogSessionManagerTest.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
2 changes: 1 addition & 1 deletion PostHog/Autocapture/PostHogAutocaptureEventTracker.swift
Original file line number Diff line number Diff line change
@@ -68,7 +68,7 @@
private static func unswizzle() {
guard hasSwizzled else { return }
hasSwizzled = false
swizzleMethods() // swizzling again will excahnge implementations back to original
swizzleMethods() // swizzling again will exchange implementations back to original
unregisterNotifications()
}

16 changes: 16 additions & 0 deletions PostHog/DI.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// DI.swift
// PostHog
//
// Created by Yiannis Josephides on 17/12/2024.
//

// swiftlint:disable:next type_name
enum DI {
static var main = Container()

final class Container {
lazy var appLifecyclePublisher: AppLifecyclePublishing = ApplicationLifecyclePublisher.shared
lazy var sessionManager: PostHogSessionManager = .init()
}
}
10 changes: 5 additions & 5 deletions PostHog/PostHogFeatureFlags.swift
Original file line number Diff line number Diff line change
@@ -31,10 +31,10 @@ class PostHogFeatureFlags {
self.storage = storage
self.api = api

preloadSesssionReplayFlag()
preloadSessionReplayFlag()
}

private func preloadSesssionReplayFlag() {
private func preloadSessionReplayFlag() {
var sessionReplay: [String: Any]?
var featureFlags: [String: Any]?
featureFlagsLock.withLock {
@@ -70,7 +70,7 @@ class PostHogFeatureFlags {
// check for multi flag variant (any)
// if let linkedFlag = sessionRecording["linkedFlag"] as? String,
// featureFlags[linkedFlag] != nil
// is also a valid check bbut since we cannot check the value of the flag,
// is also a valid check but since we cannot check the value of the flag,
// we consider session recording is active

return recordingActive
@@ -117,7 +117,7 @@ class PostHogFeatureFlags {
} else if let sessionRecording = data?["sessionRecording"] as? [String: Any] {
// keeps the value from config.sessionReplay since having sessionRecording
// means its enabled on the project settings, but its only enabled
// when local config.sessionReplay is also enabled
// when local replay integration is enabled/active
if let endpoint = sessionRecording["endpoint"] as? String {
self.config.snapshotEndpoint = endpoint
}
@@ -242,7 +242,7 @@ class PostHogFeatureFlags {
hedgeLog("Error parsing the object \(String(describing: value)): \(error)")
}

// fallbak to original value if not possible to serialize
// fallback to original value if not possible to serialize
return value
}

2 changes: 1 addition & 1 deletion PostHog/PostHogQueue.swift
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@ import Foundation
The queue uses File persistence. This allows us to
1. Only send events when we have a network connection
2. Ensure that we can survive app closing or offline situations
3. Not hold too much in mempory
3. Not hold too much in memory

*/

Loading

0 comments on commit bd28e78

Please sign in to comment.