Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
90 commits
Select commit Hold shift + click to select a range
98fdeb3
Fix concurrency gap in UIImage's environment-dependent inits
colemancda Jul 14, 2026
6cbb5c6
Fix concurrency gap in _AnimationModifier's Equatable witnesses
colemancda Jul 14, 2026
1c9ac3f
Fix concurrency gap in AnyLayoutBox's animatableData requirement
colemancda Jul 14, 2026
93f2142
Fix concurrency gap in _OpacityEffect's animatableData
colemancda Jul 14, 2026
24571a5
Fix concurrency gap in _RotationEffect's animatableData
colemancda Jul 14, 2026
b37b012
Fix concurrency gap in _VariadicView_Children.Element's Identifiable
colemancda Jul 14, 2026
c441338
Fix concurrency gap in _Button's @State isPressed
colemancda Jul 14, 2026
f22c257
Fix concurrency gap in _PickerContainer's @Binding selection
colemancda Jul 14, 2026
619857c
Fix concurrency gap in Toggle's @Binding isOn
colemancda Jul 14, 2026
571695a
Fix concurrency gap in Image's Equatable conformance
colemancda Jul 14, 2026
91e3283
Fix concurrency gap in _FoundationProgressView's @State
colemancda Jul 14, 2026
403d101
Fix concurrency gap in NavigationView's @StateObject context
colemancda Jul 14, 2026
03007c9
Fix concurrency gap in ToolbarItem's Identifiable conformance
colemancda Jul 14, 2026
a905868
Bump deployment target to iOS 17 for Observation framework support
colemancda Jul 14, 2026
53905ae
Auto-track @Observable properties read during View.body
colemancda Jul 14, 2026
677cbbc
Add @Observable view model demo to CarInformationView
colemancda Jul 14, 2026
6467dfc
Enable approachable concurrency upcoming features
colemancda Jul 14, 2026
41e1c50
Mark CPGridButton convenience init as @MainActor
colemancda Jul 14, 2026
b910a90
Mark CPPointOfInterest convenience init as @MainActor
colemancda Jul 14, 2026
4df212d
Mark CPTextButton convenience init as @MainActor
colemancda Jul 14, 2026
38d9652
Rename AnimatableData to avoid collision with CarPlay-imported SwiftU…
colemancda Jul 14, 2026
4d6cfa1
Mark Fiber.Content visitor closures as @MainActor
colemancda Jul 14, 2026
1911a05
Bridge debugDescription to MainActor-isolated Fiber state
colemancda Jul 14, 2026
14a7d50
Mark Fiber class as @MainActor
colemancda Jul 14, 2026
12b2eeb
Mark FiberReconciler class and afterReconcile accessor as @MainActor
colemancda Jul 14, 2026
d6f81b5
Mark measureText/measureImage environment accessors as @MainActor
colemancda Jul 14, 2026
f288942
Rename AnimatableData to _AnimatableData
colemancda Jul 14, 2026
97d0619
Mark MainActor-isolated members of LayoutSubviews/LayoutSubview expli…
colemancda Jul 14, 2026
e2a7a44
Mark ViewDimensions alignment subscript as @MainActor
colemancda Jul 14, 2026
5e656b5
Mark FiberReconciler.Caches class as @MainActor
colemancda Jul 14, 2026
f8d8289
Mark ModifiedContent _makeView/_visitChildren as @MainActor
colemancda Jul 14, 2026
f517778
Mark ViewDimensions alignment guide subscripts as @MainActor
colemancda Jul 14, 2026
4e2c48c
Mark Fiber tree walk functions as @MainActor
colemancda Jul 14, 2026
4dd8f66
Rename AnimatableData to _AnimatableData
colemancda Jul 14, 2026
bf50788
Rename AnimatableData and split MainActor/nonisolated isolation
colemancda Jul 14, 2026
74ced06
Rename AnimatableData to _AnimatableData
colemancda Jul 14, 2026
1802868
Rename AnimatableData to _AnimatableData
colemancda Jul 14, 2026
d653a4c
Rename AnimatableData to _AnimatableData
colemancda Jul 14, 2026
487321c
Mark hover computed property as @MainActor
colemancda Jul 14, 2026
7a0be98
Mark ModifiedContent init as @MainActor
colemancda Jul 14, 2026
bef6ac3
Mark padding(_:) modifier as @MainActor
colemancda Jul 14, 2026
0abedc3
Rename AnimatableData and split MainActor/nonisolated isolation
colemancda Jul 14, 2026
9ce11f4
Mark task action closure @isolated(any) for Task initializer compatib…
colemancda Jul 14, 2026
7f7f9a7
Rename AnimatableData to _AnimatableData
colemancda Jul 14, 2026
e0200dd
Rename AnimatableData and split MainActor/nonisolated isolation
colemancda Jul 14, 2026
e4c5231
Mark Rerender struct as @MainActor
colemancda Jul 14, 2026
a2e0c65
Fix MainActor isolation and sending diagnostics in Color resolution
colemancda Jul 14, 2026
684fa06
Fix MainActor isolation and sending diagnostics in color box resolution
colemancda Jul 14, 2026
2e57d24
Rename AnimatableData to _AnimatableData
colemancda Jul 14, 2026
5eaaa5e
Mark List initializers as @MainActor
colemancda Jul 14, 2026
70895d7
Mark section content builders as @MainActor
colemancda Jul 14, 2026
a84381c
Mark _LinkProxy.label as @MainActor
colemancda Jul 14, 2026
5f8c4a3
Mark _DatePickerProxy properties as @MainActor
colemancda Jul 14, 2026
1ce975e
Mark _SliderProxy properties as @MainActor
colemancda Jul 14, 2026
9f3d044
Mark Image initializers and layout methods as @MainActor
colemancda Jul 14, 2026
fc25be4
Mark _LazyHGridProxy properties as @MainActor
colemancda Jul 14, 2026
2da7d78
Mark _LazyVGridProxy properties as @MainActor
colemancda Jul 14, 2026
53baef8
Mark _SecureFieldProxy properties as @MainActor
colemancda Jul 14, 2026
bca9439
Mark Text initializers and modifiers as @MainActor
colemancda Jul 14, 2026
825b239
Mark _TextEditorProxy.textBinding as @MainActor
colemancda Jul 14, 2026
2df3176
Mark _TextFieldProxy properties as @MainActor
colemancda Jul 14, 2026
b8c187b
Mark ViewBuilder result builder as @MainActor
colemancda Jul 14, 2026
c868fc4
Mark Text CarPlay build(template:before:) as @MainActor
colemancda Jul 14, 2026
ce9369d
Mark Grid.Storage.view as @MainActor
colemancda Jul 14, 2026
ea91e1d
Mark ListSection convenience init as @MainActor
colemancda Jul 14, 2026
e46dc16
Mark NavigationDestination init as @MainActor
colemancda Jul 14, 2026
983c217
Mark ToolbarContentBuilder and ToolbarItemGroup extensions as @MainActor
colemancda Jul 14, 2026
163def6
Mark _ToolbarItemProxy properties as @MainActor
colemancda Jul 14, 2026
15dd927
Mark CPPointOfInterest bridging members as @MainActor
colemancda Jul 14, 2026
51aeb14
Mark Text AnyComponent conformance as @MainActor
colemancda Jul 14, 2026
db84407
Add GitHub Actions workflow to build for iOS
colemancda Jul 14, 2026
638f1ec
Fix Xcode file references
colemancda Jul 14, 2026
ed79528
Update Swift tools version to 6.3 in Package.swift
colemancda Jul 14, 2026
2f0353a
Lower swift-tools-version to 6.2 to match CI's Xcode toolchain
colemancda Jul 14, 2026
b93148b
Fix nonisolated(unsafe) placement on @State property to satisfy stabl…
colemancda Jul 14, 2026
52530c3
Fix nonisolated(unsafe) placement on @State property to satisfy stabl…
colemancda Jul 14, 2026
f9ec85a
Fix nonisolated(unsafe) placement on @State property to satisfy stabl…
colemancda Jul 14, 2026
478efd3
Mark ToggleStyleConfiguration as nonisolated to fix @Binding property…
colemancda Jul 14, 2026
070f3d8
Fix nonisolated(unsafe) placement on @State property to satisfy stabl…
colemancda Jul 14, 2026
c5ed8b6
Fix nonisolated(unsafe) placement on @State property to satisfy stabl…
colemancda Jul 14, 2026
c37e63b
Fix nonisolated(unsafe) placement on @Binding property to satisfy sta…
colemancda Jul 14, 2026
6424fd5
Fix nonisolated(unsafe) placement on @Binding property to satisfy sta…
colemancda Jul 14, 2026
d22621b
Remove unneeded nonisolated(unsafe) from Sendable-typed @State property
colemancda Jul 14, 2026
6df1c30
Fix nonisolated(unsafe) placement on @State property to satisfy stabl…
colemancda Jul 14, 2026
43d6117
Fix nonisolated(unsafe) placement on @StateObject property to satisfy…
colemancda Jul 14, 2026
d3b318a
Pin CI to Xcode 26.6
colemancda Jul 14, 2026
20d8316
Revert to latest-stable Xcode; 26.6 not available on GitHub runners
colemancda Jul 14, 2026
b4b054e
Skip unmounted elements during queued rerender updates to prevent sta…
colemancda Jul 15, 2026
3ff7206
Enhance CPInformationTemplate actions handling with debug prints and …
colemancda Jul 15, 2026
83f76dd
Remove debug print statements from CPInformationTemplate actions mana…
colemancda Jul 15, 2026
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
28 changes: 28 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Build

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

jobs:
build-ios:
name: Build for iOS
runs-on: macos-15

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Select Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: latest-stable

- name: Build CarPlayDemo for iOS Simulator
run: |
xcodebuild build \
-project Xcode/CarPlayDemo.xcodeproj \
-scheme CarPlayDemo \
-destination 'generic/platform=iOS Simulator'
12 changes: 8 additions & 4 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// swift-tools-version: 6.0
// swift-tools-version: 6.2
import PackageDescription

let package = Package(
name: "CarPlayUI",
platforms: [.iOS(.v13)],
platforms: [.iOS(.v17)],
products: [
.library(
name: "CarPlayUI",
Expand All @@ -14,14 +14,18 @@ let package = Package(
.target(
name: "CarPlayUI",
swiftSettings: [
.swiftLanguageMode(.v6)
.swiftLanguageMode(.v6),
.enableUpcomingFeature("InferIsolatedConformances"),
.enableUpcomingFeature("NonisolatedNonsendingByDefault")
]
),
.testTarget(
name: "CarPlayUITests",
dependencies: ["CarPlayUI"],
swiftSettings: [
.swiftLanguageMode(.v6)
.swiftLanguageMode(.v6),
.enableUpcomingFeature("InferIsolatedConformances"),
.enableUpcomingFeature("NonisolatedNonsendingByDefault")
]
)
]
Expand Down
1 change: 1 addition & 0 deletions Sources/CarPlayUI/Extensions/CPGridButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import CarPlay

internal extension CPGridButton {

@MainActor
convenience init(label: ParentView, action: @escaping (CPGridButton) -> ()) {
// extract labels
var labels = label.children.compactMap {
Expand Down
1 change: 1 addition & 0 deletions Sources/CarPlayUI/Extensions/CPPointOfInterest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import CarPlay
@available(iOS 14.0, *)
internal extension CPPointOfInterest {

@MainActor
convenience init(_ view: ViewObject) {
if #available(iOS 16.0, *) {
self.init(
Expand Down
1 change: 1 addition & 0 deletions Sources/CarPlayUI/Extensions/CPTextButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import CarPlay
@available(iOS 14.0, *)
internal extension CPTextButton {

@MainActor
convenience init(button: Button<Text>) {
let title = _TextProxy(button.label).rawText
let textStyle = CPTextButtonStyle(role: button.role)
Expand Down
5 changes: 4 additions & 1 deletion Sources/CarPlayUI/Extensions/UIImage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import CarPlay

internal extension UIImage {

@MainActor
convenience init?(
_ image: _ImageProxy,
environment: EnvironmentValues = .defaultEnvironment
Expand Down Expand Up @@ -38,6 +39,7 @@ internal extension UIImage {
}
}

@MainActor
static func unsafe(
_ image: Image,
environment: EnvironmentValues = .defaultEnvironment,
Expand All @@ -46,7 +48,8 @@ internal extension UIImage {
) -> UIImage {
unsafe(_ImageProxy(image), environment: environment, file: file, line: line)
}


@MainActor
static func unsafe(
_ image: _ImageProxy,
environment: EnvironmentValues = .defaultEnvironment,
Expand Down
24 changes: 16 additions & 8 deletions Sources/CarPlayUI/TokamakCore/Animation/Animatable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
import Foundation

public protocol Animatable {
associatedtype AnimatableData: VectorArithmetic
var animatableData: Self.AnimatableData { get set }
associatedtype _AnimatableData: VectorArithmetic
var animatableData: Self._AnimatableData { get set }
}

public protocol _PrimitiveAnimatable {}
Expand All @@ -32,7 +32,7 @@ public extension Animatable where Self: VectorArithmetic {
}
}

public extension Animatable where Self.AnimatableData == EmptyAnimatableData {
public extension Animatable where Self._AnimatableData == EmptyAnimatableData {
var animatableData: EmptyAnimatableData {
@inlinable get { EmptyAnimatableData() }
// swiftlint:disable:next unused_setter_value
Expand Down Expand Up @@ -140,26 +140,34 @@ public struct AnimatablePair<First, Second>: VectorArithmetic
}

extension CGPoint: Animatable {
public var animatableData: AnimatablePair<CGFloat, CGFloat> {
public typealias _AnimatableData = AnimatablePair<CGFloat, CGFloat>
public var animatableData: _AnimatableData {
@inlinable get { .init(x, y) }
@inlinable set { (x, y) = newValue[] }
}
}

extension CGSize: Animatable {
public var animatableData: AnimatablePair<CGFloat, CGFloat> {
public typealias _AnimatableData = AnimatablePair<CGFloat, CGFloat>
public var animatableData: _AnimatableData {
@inlinable get { .init(width, height) }
@inlinable set { (width, height) = newValue[] }
}
}

extension CGRect: Animatable {
public var animatableData: AnimatablePair<CGPoint.AnimatableData, CGSize.AnimatableData> {
public typealias _AnimatableData = AnimatablePair<AnimatablePair<CGFloat, CGFloat>, AnimatablePair<CGFloat, CGFloat>>
public var animatableData: _AnimatableData {
@inlinable get {
.init(origin.animatableData, size.animatableData)
.init(
AnimatablePair(origin.x, origin.y),
AnimatablePair(size.width, size.height)
)
}
@inlinable set {
(origin.animatableData, size.animatableData) = newValue[]
let value = newValue[]
origin = .init(x: value.0[].0, y: value.0[].1)
size = .init(width: value.1[].0, height: value.1[].1)
}
}
}
13 changes: 6 additions & 7 deletions Sources/CarPlayUI/TokamakCore/Animation/Animation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,8 @@ public struct _AnimationProxy {
public struct _AnimationModifier<Value>: ViewModifier, Equatable
where Value: Equatable
{
public var animation: Animation?
public var value: Value
public nonisolated(unsafe) var animation: Animation?
public nonisolated(unsafe) var value: Value

@inlinable
public init(animation: Animation?, value: Value) {
Expand All @@ -155,10 +155,9 @@ public struct _AnimationModifier<Value>: ViewModifier, Equatable
private struct ContentWrapper: View, Equatable {
let content: Content
let animation: Animation?
let value: Value
nonisolated(unsafe) let value: Value

@State
private var lastValue: Value?
@State private nonisolated(unsafe) var lastValue: Value?

var body: some View {
content.transaction {
Expand All @@ -168,7 +167,7 @@ public struct _AnimationModifier<Value>: ViewModifier, Equatable
}
}

static func == (lhs: Self, rhs: Self) -> Bool {
nonisolated static func == (lhs: Self, rhs: Self) -> Bool {
lhs.value == rhs.value
}
}
Expand All @@ -177,7 +176,7 @@ public struct _AnimationModifier<Value>: ViewModifier, Equatable
ContentWrapper(content: content, animation: animation, value: value)
}

public static func == (lhs: Self, rhs: Self) -> Bool {
public nonisolated static func == (lhs: Self, rhs: Self) -> Bool {
lhs.value == rhs.value
&& lhs.animation == rhs.animation
}
Expand Down
6 changes: 3 additions & 3 deletions Sources/CarPlayUI/TokamakCore/Fiber/Fiber+Content.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ import Foundation
public extension FiberReconciler.Fiber {
enum Content {
/// The underlying `App` instance and a function to visit it generically.
case app(Any, visit: (AppVisitor) -> ())
case app(Any, visit: @MainActor (AppVisitor) -> ())
/// The underlying `Scene` instance and a function to visit it generically.
case scene(Any, visit: (SceneVisitor) -> ())
case scene(Any, visit: @MainActor (SceneVisitor) -> ())
/// The underlying `View` instance and a function to visit it generically.
case view(Any, visit: (ViewVisitor) -> ())
case view(Any, visit: @MainActor (ViewVisitor) -> ())
}

/// Create a `Content` value for a given `App`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@
//

extension FiberReconciler.Fiber: CustomDebugStringConvertible {
public var debugDescription: String {
let memoryAddress = String(format: "%010p", unsafeBitCast(self, to: Int.self))
if case let .view(view, _) = content,
let text = view as? Text
{
return "Text(\"\(text.storage.rawText)\") (\(memoryAddress))"
public nonisolated var debugDescription: String {
MainActor.assumeIsolated {
let memoryAddress = String(format: "%010p", unsafeBitCast(self, to: Int.self))
if case let .view(view, _) = content,
let text = view as? Text
{
return "Text(\"\(text.storage.rawText)\") (\(memoryAddress))"
}
return "\(typeInfo?.name ?? "Unknown") (\(memoryAddress))"
}
return "\(typeInfo?.name ?? "Unknown") (\(memoryAddress))"
}

private func flush(level: Int = 0) -> String {
Expand Down
1 change: 1 addition & 0 deletions Sources/CarPlayUI/TokamakCore/Fiber/Fiber.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public extension FiberReconciler {
/// After the entire tree has been traversed, the current and work in progress trees are swapped,
/// making the updated tree the current one,
/// and leaving the previous current tree available to apply future changes on.
@MainActor
final class Fiber {
weak var reconciler: FiberReconciler<Renderer>?

Expand Down
2 changes: 2 additions & 0 deletions Sources/CarPlayUI/TokamakCore/Fiber/FiberReconciler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import Combine

/// A reconciler modeled after React's
/// [Fiber reconciler](https://reactjs.org/docs/faq-internals.html#what-is-react-fiber)
@MainActor
public final class FiberReconciler<Renderer: FiberRenderer> {
/// The root node in the `Fiber` tree that represents the `View`s currently rendered on screen.

Expand Down Expand Up @@ -283,6 +284,7 @@ public extension EnvironmentValues {
static let defaultValue: (@escaping () -> ()) -> () = { _ in }
}

@MainActor
var afterReconcile: (@escaping () -> ()) -> () {
get { self[AfterReconcileKey.self] }
set { self[AfterReconcileKey.self] = newValue }
Expand Down
2 changes: 2 additions & 0 deletions Sources/CarPlayUI/TokamakCore/Fiber/FiberRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ extension EnvironmentValues {
}
}

@MainActor
var measureText: (Text, ProposedViewSize, EnvironmentValues) -> CGSize {
get { self[MeasureTextKey.self] }
set { self[MeasureTextKey.self] = newValue }
Expand All @@ -139,6 +140,7 @@ extension EnvironmentValues {
}
}

@MainActor
var measureImage: (Image, ProposedViewSize, EnvironmentValues) -> CGSize {
get { self[MeasureImageKey.self] }
set { self[MeasureImageKey.self] = newValue }
Expand Down
12 changes: 6 additions & 6 deletions Sources/CarPlayUI/TokamakCore/Fiber/Layout/Layout.swift
Original file line number Diff line number Diff line change
Expand Up @@ -286,11 +286,11 @@ protocol AnyLayoutBox: AnyObject {
cache: inout Self.Cache
) -> CGFloat?

var animatableData: _AnyAnimatableData { get set }
nonisolated var animatableData: _AnyAnimatableData { get set }
}

final class ConcreteLayoutBox<L: Layout>: AnyLayoutBox {
var base: L
nonisolated(unsafe) var base: L

init(_ base: L) {
self.base = base
Expand Down Expand Up @@ -381,20 +381,20 @@ final class ConcreteLayoutBox<L: Layout>: AnyLayoutBox {
}
}

var animatableData: _AnyAnimatableData {
nonisolated var animatableData: _AnyAnimatableData {
get {
.init(base.animatableData)
}
set {
guard let newData = newValue.value as? L.AnimatableData else { return }
guard let newData = newValue.value as? L._AnimatableData else { return }
base.animatableData = newData
}
}
}

@frozen
public struct AnyLayout: Layout {
var storage: AnyLayoutBox
nonisolated(unsafe) var storage: AnyLayoutBox

public init<L>(_ layout: L) where L: Layout {
storage = ConcreteLayoutBox(layout)
Expand Down Expand Up @@ -469,7 +469,7 @@ public struct AnyLayout: Layout {
)
}

public var animatableData: _AnyAnimatableData {
public nonisolated var animatableData: _AnyAnimatableData {
get {
_AnyAnimatableData(storage.animatableData)
}
Expand Down
Loading
Loading