Merged
Merged
Conversation
Thank you for contributing to the Skip project! Please use this space to describe your change and add any labels (bug, enhancement, documentation, etc.) to help categorize your contribution.
Please review the contribution guide at https://skip.dev/docs/contributing/ for advice and guidance on making high-quality PRs.
Skip Pull Request Checklist:
- REQUIRED: I have signed the Contributor Agreement
- REQUIRED: I have tested my change locally with
swift test - OPTIONAL: I have tested my change on an iOS simulator or device
- OPTIONAL: I have tested my change on an Android emulator or device
- AI was used to generate or assist with generating this PR. Please specify below how you used AI to help you, and what steps you have taken to manually verify the changes.
Codex assisted with implementation, documentation, automated tests, generated-bridge inspection, and consumer app compile validation. The snapshot lab exposes the timing and Android capture-source controls for device comparison.
Summary
- Improves snapshot performance by making JPEG the default snapshot encoding (
.jpeg(quality: 0.85)) instead of PNG. - In local debug measurements this is typically around a 2x speedup with ~85% smaller image data.
- Removes Android's extra base64 encode/decode hop by wrapping encoded bytes directly with
Data(platformValue:). - Adds an explicit Android capture-source API:
.webViewContentusesWebView.draw(Canvas), while.visibleWindowPixelsusesPixelCopy. - Separates capture timing from capture source:
afterScreenUpdatescontrols only the optional UI-tick wait.
Breaking Changes
SkipWebSnapshot.pngDatahas been replaced withSkipWebSnapshot.imageData.- Snapshot bytes are no longer guaranteed to be PNG. Callers should use
snapshot.imageFormat.mimeTypewhen deciding how to store, upload, or display the result. - Code that still needs PNG output should request it explicitly with
imageFormat: .png. - Android no longer selects PixelCopy versus Canvas from
afterScreenUpdates, and it does not automatically fall back between capture modes.
API Shape
public struct SkipWebSnapshotImageFormat: Equatable, Sendable { public static let png: SkipWebSnapshotImageFormat public static func jpeg(quality: Double = 0.85) -> SkipWebSnapshotImageFormat public let mimeType: String public let quality: Double } public enum AndroidSnapshotCaptureMode: Hashable, Sendable { case webViewContent case visibleWindowPixels } public struct SkipWebSnapshotConfiguration: Sendable { public var afterScreenUpdates: Bool public var androidCaptureMode: AndroidSnapshotCaptureMode public var imageFormat: SkipWebSnapshotImageFormat } public struct SkipWebSnapshot: Sendable { public let imageData: Data public let imageFormat: SkipWebSnapshotImageFormat }
.webViewContent is the default when androidCaptureMode is omitted.
Android Capture Modes
| Mode | Captures |
|---|---|
.webViewContent |
The WebView's rendered content, using WebView.draw(Canvas). The capture compensates for the current scroll offset and excludes other window content, including overlays and blur effects. Some video, surface, or other compositor-backed content may not appear. |
.visibleWindowPixels |
The final pixels currently composited inside the WebView's window rectangle, using PixelCopy. This includes WebView content and anything drawn over it, including overlays and blur effects. |
Usage Example
let snapshot = try await navigator.takeSnapshot( configuration: SkipWebSnapshotConfiguration( snapshotWidth: 240, afterScreenUpdates: true, androidCaptureMode: .webViewContent, imageFormat: .jpeg(quality: 0.85) ) ) let bytes = snapshot.imageData let mimeType = snapshot.imageFormat.mimeType
For PNG compatibility:
let snapshot = try await navigator.takeSnapshot( configuration: SkipWebSnapshotConfiguration(imageFormat: .png) )
Testing
swift test- Kotlin/JUnit: 86 tests, 68 passed, 18 skipped, 0 failed
- Android consumer-app compile validation
- Generated Kotlin and Swift bridge projections inspected for the new enum and configuration field
Risks / Limitations
.webViewContentmay omit some video, surface, or other compositor-backed content..visibleWindowPixelsrequires the WebView to be attached to a valid window and includes anything composited over its rectangle.- PixelCopy failure throws
WebSnapshotError.visibleWindowPixelsCaptureFailed; SkipWeb does not automatically fall back between capture modes.