Skip to content

Commit b65c4ee

Browse files
authored
Fix Xcode RC 1 issues (#478)
1 parent 9bedf1e commit b65c4ee

File tree

7 files changed

+213
-52
lines changed

7 files changed

+213
-52
lines changed

.github/workflows/ci.yml

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ jobs:
2727
- name: Select Xcode version
2828
run: sudo xcodes select 16.4
2929

30+
- name: Install Runtimes
31+
uses: nick-fields/retry@v3
32+
with:
33+
timeout_minutes: 15
34+
max_attempts: 3
35+
command: xcodebuild -downloadAllPlatforms
36+
3037
- name: Lint Podspec
3138
run: |
3239
set -eo pipefail
@@ -43,22 +50,23 @@ jobs:
4350
- platform: [iOS, 15]
4451
runtime: iOS 15.5
4552
os: macos-15
46-
xcode: 16.4
53+
xcode: 26.0
4754
install: true
4855
- platform: [iOS, 16]
4956
runtime: iOS 16.4
5057
os: macos-15
51-
xcode: 16.4
58+
xcode: 26.0
5259
install: true
5360
- platform: [iOS, 17]
5461
runtime: iOS 17.5
5562
os: macos-15
56-
xcode: 16.4
63+
xcode: 26.0
5764
install: true
5865
- platform: [iOS, 18]
59-
runtime: iOS 18.5
66+
runtime: iOS 18.6
6067
os: macos-15
61-
xcode: 16.4
68+
xcode: 26.0
69+
install: false
6270
# - platform: [iOS, 26]
6371
# runtime: iOS 26.0
6472
# os: macos-15
@@ -67,22 +75,23 @@ jobs:
6775
- platform: [tvOS, 15]
6876
runtime: tvOS 15.4
6977
os: macos-15
70-
xcode: 16.4
78+
xcode: 26.0
7179
install: true
7280
- platform: [tvOS, 16]
7381
runtime: tvOS 16.4
7482
os: macos-15
75-
xcode: 16.4
83+
xcode: 26.0
7684
install: true
7785
- platform: [tvOS, 17]
7886
runtime: tvOS 17.5
7987
os: macos-15
80-
xcode: 16.4
88+
xcode: 26.0
8189
install: true
8290
- platform: [tvOS, 18]
8391
runtime: tvOS 18.5
8492
os: macos-15
85-
xcode: 16.4
93+
xcode: 26.0
94+
install: false
8695
# - platform: [tvOS, 26]
8796
# runtime: tvOS 26.0
8897
# os: macos-15
@@ -91,22 +100,23 @@ jobs:
91100
- platform: [watchOS, 8]
92101
runtime: watchOS 8.5
93102
os: macos-15
94-
xcode: 16.4
103+
xcode: 26.0
95104
install: true
96105
- platform: [watchOS, 9]
97106
runtime: watchOS 9.4
98107
os: macos-15
99-
xcode: 16.4
108+
xcode: 26.0
100109
install: true
101110
- platform: [watchOS, 10]
102111
runtime: watchOS 10.5
103112
os: macos-15
104-
xcode: 16.4
113+
xcode: 26.0
105114
install: true
106115
- platform: [watchOS, 11]
107116
runtime: watchOS 11.5
108117
os: macos-15
109-
xcode: 16.4
118+
xcode: 26.0
119+
install: false
110120
# - platform: [watchOS, 26]
111121
# runtime: watchOS 26.0
112122
# os: macos-15
@@ -115,7 +125,7 @@ jobs:
115125
- platform: [macOS, 15]
116126
runtime: macOS 15
117127
os: macos-15
118-
xcode: 16.4
128+
xcode: 26.0
119129
# - platform: [macOS, 26]
120130
# runtime: macOS 26.0
121131
# os: macos-15
@@ -124,12 +134,13 @@ jobs:
124134
- platform: [visionOS, 1]
125135
runtime: visionOS 1.2
126136
os: macos-15
127-
xcode: 16.4
137+
xcode: 26.0
128138
install: true
129139
- platform: [visionOS, 2]
130140
runtime: visionOS 2.5
131141
os: macos-15
132-
xcode: 16.4
142+
xcode: 26.0
143+
install: false
133144
# - platform: [visionOS, 26]
134145
# runtime: visionOS 26.0
135146
# os: macos-15
@@ -141,9 +152,17 @@ jobs:
141152
- name: Set environment variables
142153
run: echo "SKIP_SLOW_FASTLANE_WARNING=1" >> $GITHUB_ENV
143154

144-
- name: Select Xcode version
155+
- name: Select Xcode ${{ matrix.xcode }}
145156
run: sudo xcodes select ${{ matrix.xcode }}
146157

158+
- if: ${{ matrix.xcode == '26.0' && matrix.platform[0] != 'macOS' }}
159+
name: Install 2026 Runtime
160+
uses: nick-fields/retry@v3
161+
with:
162+
timeout_minutes: 15
163+
max_attempts: 3
164+
command: xcodebuild -downloadPlatform ${{ matrix.platform[0] }}
165+
147166
- if: ${{ matrix.install }}
148167
name: "[Debug] List Available Installable Runtimes"
149168
run: xcodes runtimes --include-betas

Sources/Introspect.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ struct IntrospectModifier<SwiftUIViewType: IntrospectableViewType, PlatformSpeci
107107
}
108108

109109
@MainActor
110-
public protocol PlatformEntity: AnyObject, Sendable {
110+
public protocol PlatformEntity: AnyObject {
111111
associatedtype Base: PlatformEntity
112112

113113
@_spi(Internals)

Sources/ViewTypes/SearchField.swift

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#if !os(watchOS)
22
/// An abstract representation of the search field displayed via the `.searchable` modifier in SwiftUI.
33
///
4-
/// ### iOS
4+
/// ### iOS 15 - 18
55
///
66
/// ```swift
77
/// struct ContentView: View {
@@ -13,7 +13,40 @@
1313
/// .searchable(text: $searchTerm)
1414
/// }
1515
/// .navigationViewStyle(.stack)
16-
/// .introspect(.searchField, on: .iOS(.v15, .v16, .v17, .v18, .v26)) {
16+
/// .introspect(.searchField, on: .iOS(.v15, .v16, .v17, .v18)) {
17+
/// print(type(of: $0)) // UISearchBar
18+
/// }
19+
/// }
20+
/// }
21+
/// ```
22+
///
23+
/// ### iOS 26+
24+
///
25+
/// From iOS 26 onward, search bar is only backed by UIKit when `.searchable` is used within a
26+
/// `NavigationView` or `NavigationStack` contained inside a `TabView`.
27+
///
28+
/// If `.searchable` is used outside of these containers, it is backed by SwiftUI's own implementation,
29+
/// and there is no UIKit view to introspect.
30+
///
31+
/// The only exception to this is on iPad, where double column `NavigationView` and `NavigationSplitView`
32+
/// still use `UISearchBar` even outside of a `TabView` (for now...).
33+
///
34+
/// ```swift
35+
/// struct ContentView: View {
36+
/// @State var searchTerm = ""
37+
///
38+
/// var body: some View {
39+
/// TabView {
40+
/// NavigationView {
41+
/// Text("Root")
42+
/// .searchable(text: $searchTerm)
43+
/// }
44+
/// .navigationViewStyle(.stack)
45+
/// .tabItem {
46+
/// Label("Home", systemImage: "house")
47+
/// }
48+
/// }
49+
/// .introspect(.searchField, on: .iOS(.v26)) {
1750
/// print(type(of: $0)) // UISearchBar
1851
/// }
1952
/// }

Sources/ViewTypes/TabView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
/// }
3434
/// ```
3535
///
36-
/// ### macOS
36+
/// ### macOS 10.15 - 14
3737
///
3838
/// ```swift
3939
/// struct ContentView: View {

Tests/Tests/TestUtils.swift

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,14 @@ enum TestUtils {
4646
@discardableResult
4747
func introspection<Entity: AnyObject & Sendable>(
4848
of type: Entity.Type,
49+
timeout: TimeInterval = 3,
50+
sourceLocation: SourceLocation = #_sourceLocation,
4951
@ViewBuilder view: (
5052
_ spy1: @escaping (Entity) -> Void
5153
) -> some View
5254
) async throws -> Entity {
5355
var entity1: Entity?
54-
return try await confirmation(expectedCount: 1...) { confirmation1 in
56+
return try await confirmation(expectedCount: 1..., sourceLocation: sourceLocation) { confirmation1 in
5557
let view = view(
5658
{
5759
confirmation1()
@@ -61,27 +63,33 @@ func introspection<Entity: AnyObject & Sendable>(
6163

6264
TestUtils.present(view: view)
6365

64-
while entity1 == nil {
66+
let startInstant = Date()
67+
while
68+
Date().timeIntervalSince(startInstant) < timeout,
69+
entity1 == nil
70+
{
6571
await Task.yield()
6672
}
6773

68-
return try #require(entity1)
74+
return try #require(entity1, sourceLocation: sourceLocation)
6975
}
7076
}
7177

7278
@MainActor
7379
@discardableResult
7480
func introspection<Entity: AnyObject & Sendable>(
7581
of type: Entity.Type,
82+
timeout: TimeInterval = 3,
83+
sourceLocation: SourceLocation = #_sourceLocation,
7684
@ViewBuilder view: (
7785
_ spy1: @escaping (Entity) -> Void,
7886
_ spy2: @escaping (Entity) -> Void
7987
) -> some View
8088
) async throws -> (Entity, Entity) {
8189
var entity1: Entity?
8290
var entity2: Entity?
83-
return try await confirmation(expectedCount: 1...) { confirmation1 in
84-
try await confirmation(expectedCount: 1...) { confirmation2 in
91+
return try await confirmation(expectedCount: 1..., sourceLocation: sourceLocation) { confirmation1 in
92+
try await confirmation(expectedCount: 1..., sourceLocation: sourceLocation) { confirmation2 in
8593
let view = view(
8694
{
8795
confirmation1()
@@ -95,16 +103,18 @@ func introspection<Entity: AnyObject & Sendable>(
95103

96104
TestUtils.present(view: view)
97105

106+
let startInstant = Date()
98107
while
108+
Date().timeIntervalSince(startInstant) < timeout,
99109
entity1 == nil ||
100110
entity2 == nil
101111
{
102112
await Task.yield()
103113
}
104114

105115
return try (
106-
#require(entity1),
107-
#require(entity2),
116+
#require(entity1, sourceLocation: sourceLocation),
117+
#require(entity2, sourceLocation: sourceLocation),
108118
)
109119
}
110120
}
@@ -114,6 +124,8 @@ func introspection<Entity: AnyObject & Sendable>(
114124
@discardableResult
115125
func introspection<Entity: AnyObject & Sendable>(
116126
of type: Entity.Type,
127+
timeout: TimeInterval = 3,
128+
sourceLocation: SourceLocation = #_sourceLocation,
117129
@ViewBuilder view: (
118130
_ spy1: @escaping (Entity) -> Void,
119131
_ spy2: @escaping (Entity) -> Void,
@@ -123,9 +135,9 @@ func introspection<Entity: AnyObject & Sendable>(
123135
var entity1: Entity?
124136
var entity2: Entity?
125137
var entity3: Entity?
126-
return try await confirmation(expectedCount: 1...) { confirmation1 in
127-
try await confirmation(expectedCount: 1...) { confirmation2 in
128-
try await confirmation(expectedCount: 1...) { confirmation3 in
138+
return try await confirmation(expectedCount: 1..., sourceLocation: sourceLocation) { confirmation1 in
139+
try await confirmation(expectedCount: 1..., sourceLocation: sourceLocation) { confirmation2 in
140+
try await confirmation(expectedCount: 1..., sourceLocation: sourceLocation) { confirmation3 in
129141
let view = view(
130142
{
131143
confirmation1()
@@ -143,7 +155,9 @@ func introspection<Entity: AnyObject & Sendable>(
143155

144156
TestUtils.present(view: view)
145157

158+
let startInstant = Date()
146159
while
160+
Date().timeIntervalSince(startInstant) < timeout,
147161
entity1 == nil ||
148162
entity2 == nil ||
149163
entity3 == nil
@@ -152,9 +166,9 @@ func introspection<Entity: AnyObject & Sendable>(
152166
}
153167

154168
return try (
155-
#require(entity1),
156-
#require(entity2),
157-
#require(entity3),
169+
#require(entity1, sourceLocation: sourceLocation),
170+
#require(entity2, sourceLocation: sourceLocation),
171+
#require(entity3, sourceLocation: sourceLocation),
158172
)
159173
}
160174
}
@@ -165,6 +179,8 @@ func introspection<Entity: AnyObject & Sendable>(
165179
@discardableResult
166180
func introspection<Entity: AnyObject & Sendable>(
167181
of type: Entity.Type,
182+
timeout: TimeInterval = 3,
183+
sourceLocation: SourceLocation = #_sourceLocation,
168184
@ViewBuilder view: (
169185
_ spy1: @escaping (Entity) -> Void,
170186
_ spy2: @escaping (Entity) -> Void,
@@ -176,10 +192,10 @@ func introspection<Entity: AnyObject & Sendable>(
176192
var entity2: Entity?
177193
var entity3: Entity?
178194
var entity4: Entity?
179-
return try await confirmation(expectedCount: 1...) { confirmation1 in
180-
try await confirmation(expectedCount: 1...) { confirmation2 in
181-
try await confirmation(expectedCount: 1...) { confirmation3 in
182-
try await confirmation(expectedCount: 1...) { confirmation4 in
195+
return try await confirmation(expectedCount: 1..., sourceLocation: sourceLocation) { confirmation1 in
196+
try await confirmation(expectedCount: 1..., sourceLocation: sourceLocation) { confirmation2 in
197+
try await confirmation(expectedCount: 1..., sourceLocation: sourceLocation) { confirmation3 in
198+
try await confirmation(expectedCount: 1..., sourceLocation: sourceLocation) { confirmation4 in
183199
let view = view(
184200
{
185201
confirmation1()
@@ -201,7 +217,9 @@ func introspection<Entity: AnyObject & Sendable>(
201217

202218
TestUtils.present(view: view)
203219

220+
let startInstant = Date()
204221
while
222+
Date().timeIntervalSince(startInstant) < timeout,
205223
entity1 == nil ||
206224
entity2 == nil ||
207225
entity3 == nil ||
@@ -211,10 +229,10 @@ func introspection<Entity: AnyObject & Sendable>(
211229
}
212230

213231
return try (
214-
#require(entity1),
215-
#require(entity2),
216-
#require(entity3),
217-
#require(entity4),
232+
#require(entity1, sourceLocation: sourceLocation),
233+
#require(entity2, sourceLocation: sourceLocation),
234+
#require(entity3, sourceLocation: sourceLocation),
235+
#require(entity4, sourceLocation: sourceLocation),
218236
)
219237
}
220238
}

0 commit comments

Comments
 (0)