mirror of
https://github.com/block/buzz.git
synced 2026-11-08 02:11:36 +00:00
Fence Duo APIs by UIKit SDK availability
Keep the standard navigation and sheet paths compilable with older SDKs. Exercise native fallback behavior and include the status editor in the native control harness. Co-authored-by: Terp <5856f6900e8e0a4e0ea1f4f8d2ee33e4753d2119bc122f94ecafc660b46c0511@buzz.block.builderlab.xyz> Signed-off-by: kenny lopez <klopez4212@gmail.com>
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
import Flutter
|
||||
import UIKit
|
||||
|
||||
// UIKit 9127.0.85 is the iOS 27.1 SDK with Duo APIs. SDK checks must
|
||||
// surround runtime availability checks: older SDKs cannot resolve these symbols.
|
||||
|
||||
/// Each Flutter route owns a UIKit navigation controller. Its private scroll
|
||||
/// view mirrors the Flutter viewport so UIKit performs the large-title layout.
|
||||
final class IosNavigationBarFactory: NSObject, FlutterPlatformViewFactory {
|
||||
@@ -316,6 +319,7 @@ private final class IosNavigationBarView: NSObject, FlutterPlatformView {
|
||||
result(nil)
|
||||
}
|
||||
configure(args as? [String: Any] ?? [:])
|
||||
#if canImport(UIKit, _version: 9127.0.85)
|
||||
if #available(iOS 27.1, *), standaloneControl {
|
||||
container.addInteraction(UIHingeInteraction { [weak self] _, _ in
|
||||
guard let self else { return }
|
||||
@@ -327,6 +331,7 @@ private final class IosNavigationBarView: NSObject, FlutterPlatformView {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1.2, execute: refresh)
|
||||
})
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
func view() -> UIView { container }
|
||||
@@ -421,6 +426,7 @@ private final class IosNavigationBarView: NSObject, FlutterPlatformView {
|
||||
}
|
||||
|
||||
private func reportRailGeometry() {
|
||||
#if canImport(UIKit, _version: 9127.0.85)
|
||||
guard #available(iOS 27.1, *), let window = container.window else { return }
|
||||
let inset = window.safeAreaInsets
|
||||
let right = inset.right > 0 && inset.left == 0
|
||||
@@ -458,6 +464,7 @@ private final class IosNavigationBarView: NSObject, FlutterPlatformView {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.channel.invokeMethod("railGeometry", arguments: values)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private func reportAvatarBounds() {
|
||||
@@ -872,9 +879,11 @@ private final class IosNavigationBarView: NSObject, FlutterPlatformView {
|
||||
stack.bottomAnchor.constraint(equalTo: surface.contentView.bottomAnchor)
|
||||
])
|
||||
let item = UIBarButtonItem(customView: surface)
|
||||
#if canImport(UIKit, _version: 9127.0.85)
|
||||
if #available(iOS 27.1, *), actions.allSatisfy({ $0["keepInHeaderOnDuo"] as? Bool == true }) {
|
||||
item.axisBehavior = .horizontalOnly
|
||||
}
|
||||
#endif
|
||||
if #available(iOS 26.0, *) { item.hidesSharedBackground = true }
|
||||
return item
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import Flutter
|
||||
import UIKit
|
||||
|
||||
// UIKit 9127.0.85 is the iOS 27.1 SDK with Duo APIs. SDK checks must
|
||||
// surround runtime availability checks: older SDKs cannot resolve these symbols.
|
||||
|
||||
final class NativeProfileTextEditorCoordinator: NSObject,
|
||||
UIAdaptivePresentationControllerDelegate
|
||||
{
|
||||
@@ -207,11 +210,13 @@ final class NativeProfileTextEditorCoordinator: NSObject,
|
||||
|
||||
@MainActor
|
||||
private func configureDuoPlacement(_ navigation: UINavigationController, presenter: UIViewController) {
|
||||
#if canImport(UIKit, _version: 9127.0.85)
|
||||
guard #available(iOS 27.1, *) else { return }
|
||||
let insets = presenter.view.safeAreaInsets
|
||||
guard insets.top == 0, insets.left > 0 || insets.right > 0 else { return }
|
||||
navigation.sheetPresentationController?.preferredPlacement =
|
||||
presenter.view.effectiveUserInterfaceLayoutDirection == .rightToLeft ? .leading : .trailing
|
||||
#endif
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@@ -432,10 +437,12 @@ final class NativeProfileTextEditorViewController:
|
||||
navigationItem.rightBarButtonItem?.image = UIImage(systemName: "checkmark")
|
||||
navigationItem.rightBarButtonItem?.accessibilityLabel = "Create"
|
||||
}
|
||||
#if canImport(UIKit, _version: 9127.0.85)
|
||||
if #available(iOS 27.1, *) {
|
||||
navigationItem.leftBarButtonItem?.axisBehavior = .verticalPreferred
|
||||
navigationItem.rightBarButtonItem?.axisBehavior = .verticalPreferred
|
||||
}
|
||||
#endif
|
||||
updateNavigation()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import UIKit
|
||||
|
||||
// UIKit 9127.0.85 is the iOS 27.1 SDK with Duo APIs. SDK checks must
|
||||
// surround runtime availability checks: older SDKs cannot resolve these symbols.
|
||||
|
||||
/// Status uses the same navigation-controller sheet as the profile text forms.
|
||||
final class NativeStatusEditorViewController: UITableViewController, UITextFieldDelegate {
|
||||
let statusField = UITextField()
|
||||
@@ -48,10 +51,12 @@ final class NativeStatusEditorViewController: UITableViewController, UITextField
|
||||
navigationItem.leftBarButtonItem?.accessibilityLabel = "Close"
|
||||
navigationItem.rightBarButtonItem = UIBarButtonItem(
|
||||
title: "Set", style: .done, target: self, action: #selector(save))
|
||||
#if canImport(UIKit, _version: 9127.0.85)
|
||||
if #available(iOS 27.1, *) {
|
||||
navigationItem.leftBarButtonItem?.axisBehavior = .verticalPreferred
|
||||
navigationItem.rightBarButtonItem?.axisBehavior = .verticalPreferred
|
||||
}
|
||||
#endif
|
||||
statusField.text = configuration["text"] as? String ?? ""
|
||||
statusField.placeholder = "What’s your status?"
|
||||
statusField.accessibilityLabel = "Status"
|
||||
|
||||
@@ -7,7 +7,6 @@ import XCTest
|
||||
final class ProfileSetupRecoveryTests: XCTestCase {
|
||||
@MainActor
|
||||
func testNativeEditorsPreferRightPaneOnlyWithDuoRail() async throws {
|
||||
guard #available(iOS 27.1, *) else { return }
|
||||
for duo in [false, true] {
|
||||
for method in ["presentStatus", "present"] {
|
||||
let messenger = ProfileSetupTestMessenger()
|
||||
@@ -26,7 +25,12 @@ final class ProfileSetupRecoveryTests: XCTestCase {
|
||||
DispatchQueue.main.async { continuation.resume() }
|
||||
}
|
||||
let sheet = try XCTUnwrap(parent.captured?.sheetPresentationController)
|
||||
XCTAssertEqual(sheet.preferredPlacement, duo ? .trailing : .automatic)
|
||||
#if canImport(UIKit, _version: 9127.0.85)
|
||||
if #available(iOS 27.1, *) {
|
||||
XCTAssertEqual(sheet.preferredPlacement, duo ? .trailing : .automatic)
|
||||
}
|
||||
#endif
|
||||
XCTAssertEqual(sheet.detents.map(\.identifier), [.large])
|
||||
XCTAssertNil(sheet.sourceView)
|
||||
}
|
||||
}
|
||||
@@ -56,10 +60,12 @@ final class ProfileSetupRecoveryTests: XCTestCase {
|
||||
let submit = try XCTUnwrap(editor.navigationItem.rightBarButtonItem)
|
||||
XCTAssertEqual(cancel.accessibilityLabel, "Close")
|
||||
XCTAssertFalse(submit.isEnabled)
|
||||
#if canImport(UIKit, _version: 9127.0.85)
|
||||
if #available(iOS 27.1, *) {
|
||||
XCTAssertEqual(cancel.axisBehavior, .verticalPreferred)
|
||||
XCTAssertEqual(submit.axisBehavior, .verticalPreferred)
|
||||
}
|
||||
#endif
|
||||
XCTAssertEqual(editor.tableView(editor.tableView, numberOfRowsInSection: 1), 2)
|
||||
editor.tableView(editor.tableView, didSelectRowAt: IndexPath(row: 0, section: 2))
|
||||
XCTAssertTrue(submit.isEnabled)
|
||||
@@ -94,10 +100,12 @@ final class ProfileSetupRecoveryTests: XCTestCase {
|
||||
editor.loadViewIfNeeded()
|
||||
let cancel = try XCTUnwrap(editor.navigationItem.leftBarButtonItem)
|
||||
let submit = try XCTUnwrap(editor.navigationItem.rightBarButtonItem)
|
||||
#if canImport(UIKit, _version: 9127.0.85)
|
||||
if #available(iOS 27.1, *) {
|
||||
XCTAssertEqual(cancel.axisBehavior, .verticalPreferred)
|
||||
XCTAssertEqual(submit.axisBehavior, .verticalPreferred)
|
||||
}
|
||||
#endif
|
||||
XCTAssertTrue(cancel.isEnabled)
|
||||
XCTAssertFalse(submit.isEnabled)
|
||||
UIApplication.shared.sendAction(try XCTUnwrap(cancel.action), to: cancel.target, from: cancel, for: nil)
|
||||
|
||||
@@ -659,7 +659,6 @@ class RunnerTests: XCTestCase {
|
||||
|
||||
@MainActor
|
||||
func testStandaloneRailObservesBookPosture() throws {
|
||||
guard #available(iOS 27.1, *) else { return }
|
||||
for standalone in [false, true] {
|
||||
let parent = UIViewController()
|
||||
let bar = IosNavigationBarFactory(
|
||||
@@ -667,8 +666,16 @@ class RunnerTests: XCTestCase {
|
||||
).create(withFrame: CGRect(x: 0, y: 0, width: 48, height: 48),
|
||||
viewIdentifier: 999989,
|
||||
arguments: ["standaloneControl": standalone])
|
||||
XCTAssertEqual(
|
||||
bar.view().interactions.contains { $0 is UIHingeInteraction }, standalone)
|
||||
#if canImport(UIKit, _version: 9127.0.85)
|
||||
if #available(iOS 27.1, *) {
|
||||
XCTAssertEqual(
|
||||
bar.view().interactions.contains { $0 is UIHingeInteraction }, standalone)
|
||||
} else {
|
||||
XCTAssertTrue(bar.view().interactions.isEmpty)
|
||||
}
|
||||
#else
|
||||
XCTAssertTrue(bar.view().interactions.isEmpty)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ fixture=$(mktemp -d)
|
||||
trap 'rm -r "$fixture"' EXIT
|
||||
# Compile in one file to exercise the private production controller directly.
|
||||
cat "$repo_root/mobile/ios/Runner/MeAvatarLoader.swift" \
|
||||
"$repo_root/mobile/ios/Runner/NativeStatusEditor.swift" \
|
||||
"$repo_root/mobile/ios/Runner/NativeProfileTextEditor.swift" \
|
||||
"$repo_root/mobile/ios/NativeControlTests/MeAgentAccessibilityTests.swift" > "$fixture/test.swift"
|
||||
xcrun --sdk iphonesimulator swiftc -parse-as-library \
|
||||
|
||||
Reference in New Issue
Block a user