Skip to content

Commit 684393d

Browse files
committed
Merge remote-tracking branch 'github/expose-observe-function' into expose-observe-function
# Conflicts: # Sources/SwiftNavigation/Observe.swift
2 parents ef2926b + a1573d6 commit 684393d

File tree

23 files changed

+830
-553
lines changed

23 files changed

+830
-553
lines changed

.github/workflows/ci.yml

Lines changed: 51 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
strategy:
2020
matrix:
2121
xcode:
22-
- '16.2'
22+
- '16.4'
2323
variation:
2424
- ios
2525
- macos
@@ -36,74 +36,55 @@ jobs:
3636
- name: Run tests
3737
run: make test-${{ matrix.variation }}
3838

39-
library-15-4-compatibility:
40-
runs-on: macos-14
41-
strategy:
42-
matrix:
43-
xcode:
44-
- '15.4'
45-
ios_version:
46-
- '17.5'
47-
variation:
48-
- ios
49-
steps:
50-
- uses: actions/checkout@v4
51-
- name: Select Xcode ${{ matrix.xcode }}
52-
run: sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode }}.app
53-
- name: Skip macro validation
54-
run: defaults write com.apple.dt.Xcode IDESkipMacroFingerprintValidation -bool YES
55-
- name: Run tests
56-
run: make IOS_VERSION=${{matrix.ios_version}} test-${{ matrix.variation }}
39+
# library-evolution:
40+
# name: Library Evolution
41+
# runs-on: macos-15
42+
# strategy:
43+
# matrix:
44+
# xcode:
45+
# - '16.4'
46+
# steps:
47+
# - uses: actions/checkout@v4
48+
# - name: Select Xcode ${{ matrix.xcode }}
49+
# run: sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode }}.app
50+
# - name: Skip macro validation
51+
# run: defaults write com.apple.dt.Xcode IDESkipMacroFingerprintValidation -bool YES
52+
# - name: Build for Library Evolution
53+
# run: make build-for-library-evolution
5754

58-
library-evolution:
59-
name: Library Evolution
60-
runs-on: macos-15
61-
strategy:
62-
matrix:
63-
xcode:
64-
- '16.2'
65-
steps:
66-
- uses: actions/checkout@v4
67-
- name: Select Xcode ${{ matrix.xcode }}
68-
run: sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode }}.app
69-
- name: Skip macro validation
70-
run: defaults write com.apple.dt.Xcode IDESkipMacroFingerprintValidation -bool YES
71-
- name: Build for Library Evolution
72-
run: make build-for-library-evolution
55+
# wasm:
56+
# name: Wasm
57+
# runs-on: ubuntu-latest
58+
# steps:
59+
# - uses: actions/checkout@v4
60+
# - uses: bytecodealliance/actions/wasmtime/setup@v1
61+
# - name: Install Swift and Swift SDK for WebAssembly
62+
# run: |
63+
# PREFIX=/opt/swift
64+
# set -ex
65+
# curl -f -o /tmp/swift.tar.gz "https://download.swift.org/swift-6.0.3-release/ubuntu2204/swift-6.0.3-RELEASE/swift-6.0.3-RELEASE-ubuntu22.04.tar.gz"
66+
# sudo mkdir -p $PREFIX; sudo tar -xzf /tmp/swift.tar.gz -C $PREFIX --strip-component 1
67+
# $PREFIX/usr/bin/swift sdk install https://github.com/swiftwasm/swift/releases/download/swift-wasm-6.0.3-RELEASE/swift-wasm-6.0.3-RELEASE-wasm32-unknown-wasi.artifactbundle.zip --checksum 31d3585b06dd92de390bacc18527801480163188cd7473f492956b5e213a8618
68+
# echo "$PREFIX/usr/bin" >> $GITHUB_PATH
69+
#
70+
# - name: Build tests
71+
# run: swift build --swift-sdk wasm32-unknown-wasi --build-tests -Xlinker -z -Xlinker stack-size=$((1024 * 1024))
72+
# - name: Run tests
73+
# run: wasmtime --dir . .build/debug/swift-navigationPackageTests.wasm
7374

74-
wasm:
75-
name: Wasm
76-
runs-on: ubuntu-latest
77-
steps:
78-
- uses: actions/checkout@v4
79-
- uses: bytecodealliance/actions/wasmtime/setup@v1
80-
- name: Install Swift and Swift SDK for WebAssembly
81-
run: |
82-
PREFIX=/opt/swift
83-
set -ex
84-
curl -f -o /tmp/swift.tar.gz "https://download.swift.org/swift-6.0.3-release/ubuntu2204/swift-6.0.3-RELEASE/swift-6.0.3-RELEASE-ubuntu22.04.tar.gz"
85-
sudo mkdir -p $PREFIX; sudo tar -xzf /tmp/swift.tar.gz -C $PREFIX --strip-component 1
86-
$PREFIX/usr/bin/swift sdk install https://github.com/swiftwasm/swift/releases/download/swift-wasm-6.0.3-RELEASE/swift-wasm-6.0.3-RELEASE-wasm32-unknown-wasi.artifactbundle.zip --checksum 31d3585b06dd92de390bacc18527801480163188cd7473f492956b5e213a8618
87-
echo "$PREFIX/usr/bin" >> $GITHUB_PATH
88-
89-
- name: Build tests
90-
run: swift build --swift-sdk wasm32-unknown-wasi --build-tests -Xlinker -z -Xlinker stack-size=$((1024 * 1024))
91-
- name: Run tests
92-
run: wasmtime --dir . .build/debug/swift-navigationPackageTests.wasm
93-
94-
windows:
95-
name: Windows
96-
strategy:
97-
matrix:
98-
os: [windows-latest]
99-
config: ['debug', 'release']
100-
fail-fast: false
101-
runs-on: ${{ matrix.os }}
102-
steps:
103-
- uses: compnerd/gha-setup-swift@main
104-
with:
105-
branch: swift-5.10-release
106-
tag: 5.10-RELEASE
107-
- uses: actions/checkout@v4
108-
- name: Build
109-
run: swift build -c ${{ matrix.config }}
75+
# windows:
76+
# name: Windows
77+
# strategy:
78+
# matrix:
79+
# os: [windows-latest]
80+
# config: ['debug', 'release']
81+
# fail-fast: false
82+
# runs-on: ${{ matrix.os }}
83+
# steps:
84+
# - uses: compnerd/gha-setup-swift@main
85+
# with:
86+
# branch: swift-5.10-release
87+
# tag: 5.10-RELEASE
88+
# - uses: actions/checkout@v4
89+
# - name: Build
90+
# run: swift build -c ${{ matrix.config }}

.spi.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ builder:
66
- AppKitNavigation
77
- SwiftUINavigation
88
- UIKitNavigation
9+
platform: ios
910
swift_version: 6.0

Examples/CaseStudiesTests/NavigationPathTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -515,10 +515,10 @@ final class NavigationPathTests: XCTestCase {
515515

516516
@MainActor
517517
private protocol _ValueViewController: UIViewController {
518-
associatedtype Value
518+
associatedtype Value: Hashable
519519
var value: Value { get }
520520
}
521-
private final class ValueViewController<Value>: UIViewController, _ValueViewController {
521+
private final class ValueViewController<Value: Hashable>: UIViewController, _ValueViewController {
522522
let value: Value
523523
init(value: Value) {
524524
self.value = value

Examples/CaseStudiesTests/NavigationStackTests.swift

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,56 @@ final class NavigationStackTests: XCTestCase {
292292
await assertEventuallyEqual(nav.viewControllers.count, 5)
293293
await assertEventuallyEqual(path, [1, 2, 3, 4])
294294
}
295+
296+
@MainActor
297+
func testInteractivePopViaGestureAction() async throws {
298+
@UIBinding var path = [Int]()
299+
let nav = NavigationStackController(path: $path) {
300+
UIViewController()
301+
}
302+
nav.navigationDestination(for: Int.self) { number in
303+
ChildViewController(number: number)
304+
}
305+
try await setUp(controller: nav)
306+
307+
nav.traitCollection.push(value: 1)
308+
await assertEventuallyEqual(nav.viewControllers.count, 2)
309+
await assertEventuallyEqual(path, [1])
310+
311+
let interaction = MockInteractiveTransition()
312+
let delegate = MockNavigationControllerDelegate()
313+
delegate.interactionController = interaction
314+
nav.delegate = delegate
315+
316+
let interactionExpectation = expectation(
317+
description: "navigationController(_:interactionControllerFor:) called"
318+
)
319+
delegate.interactionExpectation = interactionExpectation
320+
321+
await MainActor.run {
322+
_ = nav.popViewController(animated: true)
323+
}
324+
325+
await fulfillment(of: [interactionExpectation], timeout: 1.0)
326+
327+
XCTAssertTrue(delegate.didCallInteractionController)
328+
XCTAssertFalse(interaction.didFinish)
329+
330+
await MainActor.run {
331+
interaction.update(0.5)
332+
interaction.finish()
333+
}
334+
335+
let predicate = NSPredicate(format: "viewControllers.@count == 1")
336+
let vcCountExpectation = XCTNSPredicateExpectation(
337+
predicate: predicate,
338+
object: nav
339+
)
340+
await fulfillment(of: [vcCountExpectation], timeout: 2.0)
341+
342+
XCTAssertTrue(interaction.didFinish)
343+
XCTAssertEqual(nav.viewControllers.count, 1)
344+
}
295345
}
296346

297347
private final class ChildViewController: UIViewController {
@@ -317,3 +367,84 @@ private final class ChildViewController: UIViewController {
317367
}
318368
}
319369
}
370+
371+
private class MockInteractiveTransition: UIPercentDrivenInteractiveTransition {
372+
private(set) var didFinish = false
373+
374+
override func finish() {
375+
super.finish()
376+
didFinish = true
377+
}
378+
}
379+
380+
private class MockAnimator: NSObject, UIViewControllerAnimatedTransitioning {
381+
let duration: TimeInterval
382+
383+
init(duration: TimeInterval = 0.25) {
384+
self.duration = duration
385+
super.init()
386+
}
387+
func transitionDuration(
388+
using transitionContext: UIViewControllerContextTransitioning?
389+
) -> TimeInterval {
390+
return duration
391+
}
392+
func animateTransition(
393+
using transitionContext: UIViewControllerContextTransitioning
394+
) {
395+
// Basic animation that moves the fromView out and the toView in.
396+
guard
397+
let container = transitionContext.containerView as UIView?,
398+
let fromVC = transitionContext.viewController(forKey: .from),
399+
let toVC = transitionContext.viewController(forKey: .to)
400+
else {
401+
transitionContext.completeTransition(false)
402+
return
403+
}
404+
405+
let fromView = fromVC.view!
406+
let toView = toVC.view!
407+
408+
// Place toView below and set starting frame
409+
let initialFrame = transitionContext.initialFrame(for: fromVC)
410+
toView.frame = initialFrame.offsetBy(dx: initialFrame.width, dy: 0)
411+
container.addSubview(toView)
412+
413+
UIView.animate(
414+
withDuration: transitionDuration(using: transitionContext),
415+
delay: 0,
416+
options: [.curveLinear]
417+
) {
418+
fromView.frame = initialFrame.offsetBy(dx: -initialFrame.width / 3.0, dy: 0)
419+
toView.frame = initialFrame
420+
} completion: { finished in
421+
let cancelled = transitionContext.transitionWasCancelled
422+
transitionContext.completeTransition(!cancelled)
423+
}
424+
}
425+
}
426+
427+
private class MockNavigationControllerDelegate: NSObject, UINavigationControllerDelegate {
428+
var interactionController: UIPercentDrivenInteractiveTransition?
429+
var interactionExpectation: XCTestExpectation?
430+
var didCallInteractionController = false
431+
432+
func navigationController(
433+
_ navigationController: UINavigationController,
434+
animationControllerFor operation: UINavigationController.Operation,
435+
from fromVC: UIViewController,
436+
to toVC: UIViewController
437+
) -> UIViewControllerAnimatedTransitioning? {
438+
return MockAnimator()
439+
}
440+
func navigationController(
441+
_ navigationController: UINavigationController,
442+
interactionControllerFor animationController: UIViewControllerAnimatedTransitioning
443+
) -> UIViewControllerInteractiveTransitioning? {
444+
didCallInteractionController = true
445+
DispatchQueue.main.async { [weak self] in
446+
self?.interactionExpectation?.fulfill()
447+
}
448+
return interactionController
449+
}
450+
}

Examples/Examples.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

Lines changed: 9 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)