diff --git a/CHANGELOG.md b/CHANGELOG.md index 2020971a43..2ed56b9fb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ via the option `swizzleClassNameExclude`. ### Improvements - Serializing profile on a BG Thread (#4377) to avoid potentially slightly blocking the main thread. +- Session Replay performance for SwiftUI (#4419) ## 8.38.0-beta.1 diff --git a/Sources/Swift/Tools/SentryViewPhotographer.swift b/Sources/Swift/Tools/SentryViewPhotographer.swift index 322a7017a8..4b93a4ab05 100644 --- a/Sources/Swift/Tools/SentryViewPhotographer.swift +++ b/Sources/Swift/Tools/SentryViewPhotographer.swift @@ -45,6 +45,7 @@ class SentryViewPhotographer: NSObject, SentryViewScreenshotProvider { dispatchQueue.dispatchAsync { let screenshot = UIGraphicsImageRenderer(size: imageSize, format: .init(for: .init(displayScale: 1))).image { context in + let imageRect = CGRect(origin: .zero, size: imageSize) context.cgContext.addRect(CGRect(origin: CGPoint.zero, size: imageSize)) context.cgContext.clip(using: .evenOdd) UIColor.blue.setStroke() @@ -52,11 +53,16 @@ class SentryViewPhotographer: NSObject, SentryViewScreenshotProvider { context.cgContext.interpolationQuality = .none image.draw(at: .zero) + var latestRegion: RedactRegion? for region in redact { let rect = CGRect(origin: CGPoint.zero, size: region.size) var transform = region.transform let path = CGPath(rect: rect, transform: &transform) + defer { latestRegion = region } + + guard region != latestRegion && imageRect.contains(path.boundingBoxOfPath) else { continue } + switch region.type { case .redact, .redactSwiftUI: (region.color ?? UIImageHelper.averageColor(of: context.currentImage, at: rect.applying(region.transform))).setFill() diff --git a/Sources/Swift/Tools/UIRedactBuilder.swift b/Sources/Swift/Tools/UIRedactBuilder.swift index 72ff985d95..c558680067 100644 --- a/Sources/Swift/Tools/UIRedactBuilder.swift +++ b/Sources/Swift/Tools/UIRedactBuilder.swift @@ -27,7 +27,7 @@ enum RedactRegionType { case redactSwiftUI } -struct RedactRegion { +struct RedactRegion: Equatable { let size: CGSize let transform: CGAffineTransform let type: RedactRegionType @@ -39,6 +39,10 @@ struct RedactRegion { self.type = type self.color = color } + + static func == (lhs: RedactRegion, rhs: RedactRegion) -> Bool { + lhs.size == rhs.size && lhs.transform == rhs.transform && lhs.type == rhs.type + } } class UIRedactBuilder { diff --git a/Tests/SentryTests/SentryViewPhotographerTests.swift b/Tests/SentryTests/SentryViewPhotographerTests.swift index 3217e9dca7..69643f483d 100644 --- a/Tests/SentryTests/SentryViewPhotographerTests.swift +++ b/Tests/SentryTests/SentryViewPhotographerTests.swift @@ -180,6 +180,21 @@ class SentryViewPhotographerTests: XCTestCase { assertColor(pixel2, .white) } + func testSkipSameRegion() throws { + let label1 = UILabel(frame: CGRect(x: 0, y: 0, width: 50, height: 25)) + label1.text = "Test" + label1.textColor = .red + + let label2 = UILabel(frame: CGRect(x: 0, y: 0, width: 50, height: 25)) + label2.text = "Test" + label2.textColor = .green + + let image = try XCTUnwrap(prepare(views: [label1, label2])) + let pixel1 = color(at: CGPoint(x: 10, y: 10), in: image) + + assertColor(pixel1, .green) + } + private func assertColor(_ color1: UIColor, _ color2: UIColor) { let sRGBColor1 = color1.cgColor.converted(to: CGColorSpace(name: CGColorSpace.sRGB)!, intent: .defaultIntent, options: nil) let sRGBColor2 = color2.cgColor.converted(to: CGColorSpace(name: CGColorSpace.sRGB)!, intent: .defaultIntent, options: nil)