Created
September 16, 2025 05:52
-
-
Save Kyle-Ye/f2787d470e31e76006a51b4bcae4c8dd to your computer and use it in GitHub Desktop.
Unit Test SwiftUI redraws demo from OpenSwiftUI code
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Testing | |
import SwiftUI | |
@testable import Demo | |
typealias PlatformViewController = UIViewController | |
typealias PlatformWindow = UIWindow | |
typealias PlatformHostingController = UIHostingController | |
extension PlatformViewController { | |
// NOTE: Remember to withExtendedLifetime for window to ensure it is not deallocated duration animation or update. | |
func triggerLayout() { | |
#if os(iOS) | |
let window = UIWindow(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) | |
window.rootViewController = self | |
window.makeKeyAndVisible() | |
view.layoutIfNeeded() | |
#else | |
let window = NSWindow( | |
contentRect: CGRect(x: 0, y: 0, width: 100, height: 100), | |
styleMask: [.titled, .closable, .resizable], | |
backing: .buffered, | |
defer: false | |
) | |
window.contentViewController = self | |
window.makeKeyAndOrderFront(nil) | |
view.layoutSubtreeIfNeeded() | |
#endif | |
} | |
} | |
@MainActor | |
func triggerLayoutWithWindow( | |
expectedCount: Int = 1, | |
_ body: @escaping @MainActor (Confirmation, UnsafeContinuation<Void, Never>) -> PlatformViewController | |
) async throws { | |
var window: PlatformWindow! | |
await confirmation(expectedCount: expectedCount) { @MainActor confirmation in | |
await withUnsafeContinuation { (continuation: UnsafeContinuation<Void, Never>) in | |
let vc = body(confirmation, continuation) | |
vc.triggerLayout() | |
window = vc.view.window | |
} | |
} | |
#if os(macOS) | |
window.isReleasedWhenClosed = false | |
window.close() | |
#endif | |
withExtendedLifetime(window) {} | |
} | |
struct DemoTests { | |
class Model: ObservableObject { | |
@Published var name = "" | |
@Published var count = 0 | |
} | |
struct ContentView: View { | |
@StateObject private var m = Model() | |
var confirmation: Confirmation | |
var continuation: UnsafeContinuation<Void, Never> | |
var body: some View { | |
let _ = confirmation() | |
VStack { | |
Text(m.name) | |
Text(m.count.description) | |
} | |
.onAppear { | |
DispatchQueue.main.async { | |
m.name = "A" | |
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { | |
m.count = 1 | |
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { | |
continuation.resume() | |
} | |
} | |
} | |
} | |
} | |
} | |
@Test | |
func observeCount() async throws { | |
try await triggerLayoutWithWindow(expectedCount: 3) { confirmation, continuation in | |
PlatformHostingController( | |
rootView: ContentView( | |
confirmation: confirmation, | |
continuation: continuation | |
) | |
) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment