From 9129000db8253f7a33f365b40caf0345bae87255 Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Wed, 27 Nov 2024 16:28:39 +0100 Subject: [PATCH 1/3] fix: flutter session replay --- CHANGELOG.md | 2 ++ PostHog.xcodeproj/project.pbxproj | 4 ++++ PostHog/PostHogSessionManager.swift | 15 +++++------- PostHog/Replay/Date+Util.swift | 2 +- PostHog/Replay/PostHogReplayIntegration.swift | 18 ++++++++++---- PostHog/Replay/RRWireframe.swift | 15 ++---------- PostHog/Replay/UIImage+Util.swift | 24 +++++++++++++++++++ 7 files changed, 52 insertions(+), 28 deletions(-) create mode 100644 PostHog/Replay/UIImage+Util.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 68d9e4539..35d828dfe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ## Next +- no user facing changes + ## 3.15.6 - 2024-11-20 - fix: read accessibilityLabel from parent's view to avoid performance hit on RN ([#259](https://github.com/PostHog/posthog-ios/pull/259)) diff --git a/PostHog.xcodeproj/project.pbxproj b/PostHog.xcodeproj/project.pbxproj index 25eed903c..c61a367ed 100644 --- a/PostHog.xcodeproj/project.pbxproj +++ b/PostHog.xcodeproj/project.pbxproj @@ -87,6 +87,7 @@ 6999919A2AFE1BAB000DCB78 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 699991992AFE1BAB000DCB78 /* AppDelegate.swift */; }; 699C5FE62C20178E007DB818 /* UUIDUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 699C5FE52C20178E007DB818 /* UUIDUtils.swift */; }; 699C5FEF2C20242A007DB818 /* UUIDTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 699C5FEE2C20242A007DB818 /* UUIDTest.swift */; }; + 69B7F60C2CF7703400A48BCC /* UIImage+Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69B7F6062CF7702D00A48BCC /* UIImage+Util.swift */; }; 69BA38D72B888E8500AA69D6 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 69BA38D62B888E8500AA69D6 /* PrivacyInfo.xcprivacy */; }; 69ED1A5C2C7F15F300FE7A91 /* PostHogSessionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69ED1A5B2C7F15F300FE7A91 /* PostHogSessionManager.swift */; }; 69ED1A882C89B73100FE7A91 /* PostHogSwiftUIViewModifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69ED1A872C89B73100FE7A91 /* PostHogSwiftUIViewModifiers.swift */; }; @@ -358,6 +359,7 @@ 699991992AFE1BAB000DCB78 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 699C5FE52C20178E007DB818 /* UUIDUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UUIDUtils.swift; sourceTree = ""; }; 699C5FEE2C20242A007DB818 /* UUIDTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UUIDTest.swift; sourceTree = ""; }; + 69B7F6062CF7702D00A48BCC /* UIImage+Util.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Util.swift"; sourceTree = ""; }; 69BA38D62B888E8500AA69D6 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 69ED1A5B2C7F15F300FE7A91 /* PostHogSessionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogSessionManager.swift; sourceTree = ""; }; 69ED1A872C89B73100FE7A91 /* PostHogSwiftUIViewModifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogSwiftUIViewModifiers.swift; sourceTree = ""; }; @@ -723,6 +725,7 @@ 69EE82B82BA9C4DA00EB9542 /* Replay */ = { isa = PBXGroup; children = ( + 69B7F6062CF7702D00A48BCC /* UIImage+Util.swift */, 69EE82B92BA9C50400EB9542 /* PostHogReplayIntegration.swift */, 69EE82BB2BA9C53000EB9542 /* PostHogSessionReplayConfig.swift */, 69EE82BD2BA9C8AA00EB9542 /* ViewLayoutTracker.swift */, @@ -1185,6 +1188,7 @@ 69ED1A9F2C8F451B00FE7A91 /* PostHogPersonProfiles.swift in Sources */, 69EE82BA2BA9C50400EB9542 /* PostHogReplayIntegration.swift in Sources */, 3AE3FB472992AB0000AFFC18 /* Hedgelog.swift in Sources */, + 69B7F60C2CF7703400A48BCC /* UIImage+Util.swift in Sources */, 69261D132AD5685B00232EC7 /* PostHogFeatureFlags.swift in Sources */, 699C5FE62C20178E007DB818 /* UUIDUtils.swift in Sources */, 690B2DF32C205B5600AE3B45 /* TimeBasedEpochGenerator.swift in Sources */, diff --git a/PostHog/PostHogSessionManager.swift b/PostHog/PostHogSessionManager.swift index c85e04e27..1d4ef2e10 100644 --- a/PostHog/PostHogSessionManager.swift +++ b/PostHog/PostHogSessionManager.swift @@ -46,14 +46,13 @@ import Foundation timeNow - sessionLastTimestamp > sessionChangeThreshold } - private func isiOSNativeSdk() -> Bool { - // postHogSdkName will be set to eg posthog-react-native if not - postHogSdkName == postHogiOSSdkName + private func isNotReactNative() -> Bool { + // for the RN SDK, the session is handled by the RN SDK itself + postHogSdkName != "posthog-react-native" } func resetSessionIfExpired(_ completion: () -> Void) { - // for hybrid SDKs, the session is handled by the hybrid SDK - guard isiOSNativeSdk() else { + guard isNotReactNative() else { return } @@ -89,8 +88,7 @@ import Foundation } func rotateSessionIdIfRequired(_ completion: @escaping (() -> Void)) { - // for hybrid SDKs, the session is handled by the hybrid SDK - guard isiOSNativeSdk() else { + guard isNotReactNative() else { return } @@ -109,8 +107,7 @@ import Foundation } func updateSessionLastTime() { - // for hybrid SDKs, the session is handled by the hybrid SDK - guard isiOSNativeSdk() else { + guard isNotReactNative() else { return } diff --git a/PostHog/Replay/Date+Util.swift b/PostHog/Replay/Date+Util.swift index dedfdc906..5ea835317 100644 --- a/PostHog/Replay/Date+Util.swift +++ b/PostHog/Replay/Date+Util.swift @@ -7,7 +7,7 @@ import Foundation -extension Date { +public extension Date { func toMillis() -> Int64 { Int64(timeIntervalSince1970 * 1000) } diff --git a/PostHog/Replay/PostHogReplayIntegration.swift b/PostHog/Replay/PostHogReplayIntegration.swift index ea22d2884..7ca74c5ab 100644 --- a/PostHog/Replay/PostHogReplayIntegration.swift +++ b/PostHog/Replay/PostHogReplayIntegration.swift @@ -96,14 +96,22 @@ } } + private func isNotFlutter() -> Bool { + postHogSdkName != "posthog-flutter" + } + func start() { stopTimer() - DispatchQueue.main.async { - self.timer = Timer.scheduledTimer(withTimeInterval: self.config.sessionReplayConfig.debouncerDelay, repeats: true, block: { _ in - self.snapshot() - }) + + // flutter captures snapshots, so we don't need to capture them here + if isNotFlutter() { + DispatchQueue.main.async { + self.timer = Timer.scheduledTimer(withTimeInterval: self.config.sessionReplayConfig.debouncerDelay, repeats: true, block: { _ in + self.snapshot() + }) + } + ViewLayoutTracker.swizzleLayoutSubviews() } - ViewLayoutTracker.swizzleLayoutSubviews() UIApplicationTracker.swizzleSendEvent() diff --git a/PostHog/Replay/RRWireframe.swift b/PostHog/Replay/RRWireframe.swift index ef5adfe32..bd6553aa1 100644 --- a/PostHog/Replay/RRWireframe.swift +++ b/PostHog/Replay/RRWireframe.swift @@ -38,17 +38,6 @@ class RRWireframe { var parentId: Int? #if os(iOS) - private func imageToBase64(_ image: UIImage) -> String? { - let jpegData = image.jpegData(compressionQuality: 0.3) - let base64 = jpegData?.base64EncodedString() - - if let base64 = base64 { - return "data:image/jpeg;base64,\(base64)" - } - - return nil - } - private func maskImage() -> UIImage? { if let image = image { // the scale also affects the image size/resolution, from usually 100kb to 15kb each @@ -106,9 +95,9 @@ class RRWireframe { #if os(iOS) if let image = image { if let maskedImage = maskImage() { - base64 = imageToBase64(maskedImage) + base64 = maskedImage.toBase64() } else { - base64 = imageToBase64(image) + base64 = image.toBase64() } } #endif diff --git a/PostHog/Replay/UIImage+Util.swift b/PostHog/Replay/UIImage+Util.swift new file mode 100644 index 000000000..471995c2a --- /dev/null +++ b/PostHog/Replay/UIImage+Util.swift @@ -0,0 +1,24 @@ +// +// UIImage+Util.swift +// PostHog +// +// Created by Manoel Aranda Neto on 27.11.24. +// + +#if os(iOS) + import Foundation + import UIKit + + public extension UIImage { + func toBase64(_ compressionQuality: CGFloat = 0.3) -> String? { + let jpegData = jpegData(compressionQuality: compressionQuality) + let base64 = jpegData?.base64EncodedString() + + if let base64 = base64 { + return "data:image/jpeg;base64,\(base64)" + } + + return nil + } + } +#endif From dfa287b22aeb3260bb1374724ee8d8fa87418505 Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Wed, 27 Nov 2024 16:31:52 +0100 Subject: [PATCH 2/3] fix --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5766d29c..8c2b62dec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -392,4 +392,4 @@ Fix issues with launching the library and screen tracking. ## 1.0.0 - 2020-04-22 -First Release. \ No newline at end of file +First Release. From 325df70b5f48595174619e9e03ec2f58bbd60921 Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Thu, 28 Nov 2024 08:23:34 +0100 Subject: [PATCH 3/3] fix --- PostHog/Replay/Date+Util.swift | 6 +++++- PostHog/Replay/UIImage+Util.swift | 6 +++++- PostHog/Utils/Data+Gzip.swift | 2 +- PostHog/Utils/Reachability.swift | 4 ++-- PostHog/Utils/UUIDUtils.swift | 2 +- 5 files changed, 14 insertions(+), 6 deletions(-) diff --git a/PostHog/Replay/Date+Util.swift b/PostHog/Replay/Date+Util.swift index 5ea835317..4cb98d9dd 100644 --- a/PostHog/Replay/Date+Util.swift +++ b/PostHog/Replay/Date+Util.swift @@ -7,8 +7,12 @@ import Foundation -public extension Date { +extension Date { func toMillis() -> Int64 { Int64(timeIntervalSince1970 * 1000) } } + +public func dateToMillis(_ date: Date) -> Int64 { + date.toMillis() +} diff --git a/PostHog/Replay/UIImage+Util.swift b/PostHog/Replay/UIImage+Util.swift index 471995c2a..d511f0447 100644 --- a/PostHog/Replay/UIImage+Util.swift +++ b/PostHog/Replay/UIImage+Util.swift @@ -9,7 +9,7 @@ import Foundation import UIKit - public extension UIImage { + extension UIImage { func toBase64(_ compressionQuality: CGFloat = 0.3) -> String? { let jpegData = jpegData(compressionQuality: compressionQuality) let base64 = jpegData?.base64EncodedString() @@ -21,4 +21,8 @@ return nil } } + + public func imageToBase64(_ image: UIImage, _ compressionQuality: CGFloat = 0.3) -> String? { + image.toBase64(compressionQuality) + } #endif diff --git a/PostHog/Utils/Data+Gzip.swift b/PostHog/Utils/Data+Gzip.swift index 37a76f34f..09498a64c 100644 --- a/PostHog/Utils/Data+Gzip.swift +++ b/PostHog/Utils/Data+Gzip.swift @@ -134,7 +134,7 @@ private extension GzipError.Kind { } } -public extension Data { +extension Data { /// Whether the receiver is compressed in gzip format. var isGzipped: Bool { starts(with: [0x1F, 0x8B]) // check magic number diff --git a/PostHog/Utils/Reachability.swift b/PostHog/Utils/Reachability.swift index 15ca1bf38..b405edcab 100644 --- a/PostHog/Utils/Reachability.swift +++ b/PostHog/Utils/Reachability.swift @@ -41,7 +41,7 @@ import Foundation @available(*, unavailable, renamed: "Notification.Name.reachabilityChanged") public let ReachabilityChangedNotification = NSNotification.Name("ReachabilityChangedNotification") - public extension Notification.Name { + extension Notification.Name { static let reachabilityChanged = Notification.Name("reachabilityChanged") } @@ -162,7 +162,7 @@ import Foundation } } - public extension Reachability { + extension Reachability { // MARK: - *** Notifier methods *** func startNotifier() throws { diff --git a/PostHog/Utils/UUIDUtils.swift b/PostHog/Utils/UUIDUtils.swift index 437ece52f..66c3dd268 100644 --- a/PostHog/Utils/UUIDUtils.swift +++ b/PostHog/Utils/UUIDUtils.swift @@ -10,7 +10,7 @@ import Foundation -public extension UUID { +extension UUID { static func v7() -> Self { TimeBasedEpochGenerator.shared.v7() }