Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,17 @@ concurrency:
cancel-in-progress: true

env:
# Pin the image (macos-15) and a GM Xcode by path. `macos-latest` +
# setup-xcode's `latest-stable` rolled onto a 26.6 Release Candidate whose
# XCTest runtime deadlocked the tests; a GM path fails loudly if the image
# drops it (bump then — never an RC).
DEVELOPER_DIR: /Applications/Xcode_26.3.app/Contents/Developer
# Pin the image (xcode-27, GitHub's image for the Xcode 27 line) and Xcode by
# path. `macos-latest` + setup-xcode's `latest-stable` rolled onto a 26.6
# Release Candidate whose XCTest runtime deadlocked the tests; a versioned path
# fails loudly if the image drops it (bump then). `Xcode_27.0.app` is the
# image's alias for the line, so it carries over when the image reaches the GM.
DEVELOPER_DIR: /Applications/Xcode_27.0.app/Contents/Developer

jobs:
build-and-unit:
name: Build + unit tests
runs-on: macos-15
runs-on: xcode-27
steps:
- uses: actions/checkout@v5

Expand Down Expand Up @@ -92,7 +93,7 @@ jobs:

ui-tests:
name: UI tests
runs-on: macos-15
runs-on: xcode-27
# Slow gate: run once a PR is ready for review (and on pushes to main), not on
# draft pushes. A skipped required check still reports success, so drafts stay
# mergeable-clean.
Expand Down
15 changes: 12 additions & 3 deletions Pinwheel/Sources/Pinwheel/Capture/PinDisplayList.swift
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,8 @@ enum PinDisplayList {
// payload without descending into child display lists.
private static func clipCornerRadius(_ payload: Any, _ depth: Int = 0) -> CGFloat? {
if depth > 8 { return nil }
if String(describing: type(of: payload)).contains("FixedRoundedRect"), let size = child(payload, "cornerSize") as? CGSize {
return size.width
if String(describing: type(of: payload)).contains("FixedRoundedRect"), let radius = fixedRoundedRectRadius(payload) {
return radius
}
for field in Mirror(reflecting: payload).children where !isDisplayList(field.value) {
if let radius = clipCornerRadius(field.value, depth + 1) { return radius }
Expand Down Expand Up @@ -307,11 +307,20 @@ enum PinDisplayList {
// SwiftUI Path.storage is an enum; `.roundedRect(FixedRoundedRect)` exposes the exact corner size.
private static func roundedRectRadius(_ path: Any?) -> CGFloat? {
guard let path, let storage = child(path, "storage"), let (kind, value) = enumCase(storage) else { return nil }
if kind == "roundedRect", let size = child(value, "cornerSize") as? CGSize { return size.width }
if kind == "roundedRect" { return fixedRoundedRectRadius(value) }
if kind == "rect" { return 0 }
return nil
}

// FixedRoundedRect carried `cornerSize: CGSize` through iOS 26 and carries `radii: Radii` from iOS 27,
// so read whichever this OS supplies. An `.uneven` radii has no single corner to report, and answering
// nil sends the shape down the rasterizing path rather than inventing a radius for it.
private static func fixedRoundedRectRadius(_ value: Any) -> CGFloat? {
if let size = child(value, "cornerSize") as? CGSize { return size.width }
guard let radii = child(value, "radii"), let (kind, corners) = enumCase(radii), kind == "uniform" else { return nil }
return corners as? CGFloat
}

private static func nestedLists(in value: Any) -> [Any] {
var found: [Any] = []
for level1 in Mirror(reflecting: value).children {
Expand Down
4 changes: 2 additions & 2 deletions Pinwheel/Sources/Pinwheel/Extensions/UIFontExtensions.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import UIKit

extension UIFont {
nonisolated extension UIFont {
public class func rounded(ofSize size: CGFloat, weight: UIFont.Weight) -> UIFont {
let systemFont = UIFont.systemFont(ofSize: size, weight: weight)
let font: UIFont
Expand All @@ -24,7 +24,7 @@ extension UIFont {
}
}

extension UIFont.Weight {
nonisolated extension UIFont.Weight {
public var symbolWeight: UIImage.SymbolWeight {
switch self {
case .ultraLight:
Expand Down
Loading