tifroz · GitHub

Merged

Merged

Conversation

@tifroz

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: .webViewContent uses WebView.draw(Canvas), while .visibleWindowPixels uses PixelCopy.
  • Separates capture timing from capture source: afterScreenUpdates controls only the optional UI-tick wait.

Breaking Changes

  • SkipWebSnapshot.pngData has been replaced with SkipWebSnapshot.imageData.
  • Snapshot bytes are no longer guaranteed to be PNG. Callers should use snapshot.imageFormat.mimeType when 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

  • .webViewContent may omit some video, surface, or other compositor-backed content.
  • .visibleWindowPixels requires 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.

@marcprux

Read the original on github.com ↗