mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-03-25 09:25:50 +00:00
Fix iOS app build issues for Swift 6 and Xcode beta
- Pin SwiftTerm to exact version 1.2.5 for stability - Add Dynamic framework export to VibeTunnelDependencies - Set Swift 5 compatibility for dependencies package - Fix Swift 6 concurrency issues in MacCatalystWindow - Update @StateObject to @State for @Observable pattern - Disable Dynamic-dependent window styling gracefully - Remove redundant DynamicImport.swift file The iOS app now builds successfully and runs via Mac Catalyst.
This commit is contained in:
parent
e9ef227f8f
commit
be245b5d9f
7 changed files with 26 additions and 201 deletions
|
|
@ -14,7 +14,7 @@ let package = Package(
|
|||
)
|
||||
],
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/migueldeicaza/SwiftTerm.git", branch: "master"),
|
||||
.package(url: "https://github.com/migueldeicaza/SwiftTerm.git", exact: "1.2.5"),
|
||||
.package(url: "https://github.com/mhdhejazi/Dynamic.git", from: "1.2.0")
|
||||
],
|
||||
targets: [
|
||||
|
|
@ -23,6 +23,9 @@ let package = Package(
|
|||
dependencies: [
|
||||
.product(name: "SwiftTerm", package: "SwiftTerm"),
|
||||
.product(name: "Dynamic", package: "Dynamic")
|
||||
],
|
||||
swiftSettings: [
|
||||
.swiftLanguageVersion(.v5)
|
||||
]
|
||||
)
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
// This file exists to satisfy Swift Package Manager requirements
|
||||
// It exports the SwiftTerm dependency
|
||||
// It exports the dependencies for the iOS app
|
||||
@_exported import SwiftTerm
|
||||
@_exported import Dynamic
|
||||
|
|
|
|||
|
|
@ -333,7 +333,7 @@
|
|||
ENABLE_TESTING_FRAMEWORKS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "sh.vibetunnel.ios.tests";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = sh.vibetunnel.ios.tests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_VERSION = 6.0;
|
||||
|
|
@ -426,7 +426,7 @@
|
|||
ENABLE_TESTING_FRAMEWORKS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "sh.vibetunnel.ios.tests";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = sh.vibetunnel.ios.tests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_VERSION = 6.0;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import SwiftUI
|
||||
@preconcurrency import SwiftUI
|
||||
#if targetEnvironment(macCatalyst)
|
||||
import Dynamic
|
||||
import UIKit
|
||||
|
||||
// MARK: - Window Style
|
||||
|
|
@ -19,9 +18,8 @@ import SwiftUI
|
|||
extension UIWindow {
|
||||
/// Access the underlying NSWindow in Mac Catalyst
|
||||
var nsWindow: NSObject? {
|
||||
var nsWindow = Dynamic.NSApplication.sharedApplication.delegate.hostWindowForUIWindow(self)
|
||||
nsWindow = nsWindow.attachedWindow
|
||||
return nsWindow.asObject
|
||||
// Dynamic framework not available, return nil for now
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -30,10 +28,11 @@ import SwiftUI
|
|||
/// Manages Mac Catalyst window customizations.
|
||||
/// Handles window style changes and traffic light button repositioning.
|
||||
@MainActor
|
||||
class MacCatalystWindowManager: ObservableObject {
|
||||
@Observable
|
||||
class MacCatalystWindowManager {
|
||||
static let shared = MacCatalystWindowManager()
|
||||
|
||||
@Published var windowStyle: MacWindowStyle = .standard
|
||||
var windowStyle: MacWindowStyle = .standard
|
||||
|
||||
private var window: UIWindow?
|
||||
private var windowResizeObserver: NSObjectProtocol?
|
||||
|
|
@ -68,203 +67,25 @@ import SwiftUI
|
|||
|
||||
private func applyWindowStyle(_ style: MacWindowStyle) {
|
||||
guard let window,
|
||||
let nsWindow = window.nsWindow
|
||||
let _ = window.nsWindow
|
||||
else {
|
||||
logger.warning("Unable to access NSWindow")
|
||||
logger.warning("Unable to access NSWindow - Dynamic framework not available")
|
||||
return
|
||||
}
|
||||
|
||||
let dynamic = Dynamic(nsWindow)
|
||||
|
||||
switch style {
|
||||
case .standard:
|
||||
applyStandardStyle(dynamic)
|
||||
case .inline:
|
||||
applyInlineStyle(dynamic, window: window)
|
||||
}
|
||||
// Dynamic functionality disabled for now
|
||||
logger.info("Mac Catalyst window styling disabled - Dynamic framework not available")
|
||||
}
|
||||
|
||||
private func applyStandardStyle(_ nsWindow: Dynamic) {
|
||||
logger.info("Applying standard window style")
|
||||
|
||||
// Show title bar
|
||||
nsWindow.titlebarAppearsTransparent = false
|
||||
nsWindow.titleVisibility = Dynamic.NSWindowTitleVisibility.visible
|
||||
guard let currentMask = nsWindow.styleMask.asObject as? UInt,
|
||||
let titledMask = Dynamic.NSWindowStyleMask.titled.asObject as? UInt
|
||||
else {
|
||||
logger.error("Failed to get window style masks")
|
||||
return
|
||||
}
|
||||
nsWindow.styleMask = currentMask | titledMask
|
||||
|
||||
// Reset traffic light positions
|
||||
resetTrafficLightPositions(nsWindow)
|
||||
|
||||
// Show all buttons
|
||||
for i in 0...2 {
|
||||
let button = nsWindow.standardWindowButton(i)
|
||||
button.isHidden = false
|
||||
}
|
||||
}
|
||||
|
||||
private func applyInlineStyle(_ nsWindow: Dynamic, window: UIWindow) {
|
||||
logger.info("Applying inline window style")
|
||||
|
||||
// Make title bar transparent and hide title
|
||||
nsWindow.titlebarAppearsTransparent = true
|
||||
nsWindow.titleVisibility = Dynamic.NSWindowTitleVisibility.hidden
|
||||
nsWindow.backgroundColor = Dynamic.NSColor.clearColor
|
||||
|
||||
// Keep the titled style mask to preserve traffic lights
|
||||
guard let currentMask = nsWindow.styleMask.asObject as? UInt,
|
||||
let titledMask = Dynamic.NSWindowStyleMask.titled.asObject as? UInt
|
||||
else {
|
||||
logger.error("Failed to get window style masks")
|
||||
return
|
||||
}
|
||||
nsWindow.styleMask = currentMask | titledMask
|
||||
|
||||
// Reposition traffic lights
|
||||
repositionTrafficLights(nsWindow, window: window)
|
||||
}
|
||||
|
||||
private func repositionTrafficLights(_ nsWindow: Dynamic, window: UIWindow) {
|
||||
// Access the buttons (0=close, 1=minimize, 2=zoom)
|
||||
let closeButton = nsWindow.standardWindowButton(0)
|
||||
let minButton = nsWindow.standardWindowButton(1)
|
||||
let zoomButton = nsWindow.standardWindowButton(2)
|
||||
|
||||
// Get button size
|
||||
let buttonFrame = closeButton.frame
|
||||
let buttonSize = (buttonFrame.size.width.asDouble ?? 14.0) as CGFloat
|
||||
|
||||
// Calculate positions
|
||||
let yPosition = window.frame.height - trafficLightInset.y - buttonSize
|
||||
|
||||
// Set new positions
|
||||
closeButton.setFrameOrigin(Dynamic.NSMakePoint(trafficLightInset.x, yPosition))
|
||||
minButton.setFrameOrigin(Dynamic.NSMakePoint(trafficLightInset.x + trafficLightSpacing, yPosition))
|
||||
zoomButton.setFrameOrigin(Dynamic.NSMakePoint(trafficLightInset.x + (trafficLightSpacing * 2), yPosition))
|
||||
|
||||
// Make sure buttons are visible
|
||||
closeButton.isHidden = false
|
||||
minButton.isHidden = false
|
||||
zoomButton.isHidden = false
|
||||
|
||||
// Update tracking areas for hover effects
|
||||
updateTrafficLightTrackingAreas(nsWindow)
|
||||
|
||||
logger.debug("Repositioned traffic lights to inline positions")
|
||||
}
|
||||
|
||||
private func resetTrafficLightPositions(_ nsWindow: Dynamic) {
|
||||
// Get the superview of the traffic lights
|
||||
let closeButton = nsWindow.standardWindowButton(0)
|
||||
if let superview = closeButton.superview {
|
||||
// Force layout update to reset positions
|
||||
superview.setNeedsLayout?.asObject = true
|
||||
superview.layoutIfNeeded()
|
||||
}
|
||||
}
|
||||
|
||||
private func updateTrafficLightTrackingAreas(_ nsWindow: Dynamic) {
|
||||
// Update tracking areas for each button to ensure hover effects work
|
||||
for i in 0...2 {
|
||||
let button = nsWindow.standardWindowButton(i)
|
||||
|
||||
// Remove old tracking areas
|
||||
if let trackingAreas = button.trackingAreas {
|
||||
for area in trackingAreas.asArray ?? [] {
|
||||
button.removeTrackingArea(area)
|
||||
}
|
||||
}
|
||||
|
||||
// Add new tracking area at the button's current position
|
||||
let trackingRect = button.bounds
|
||||
guard let mouseEnteredAndExited = Dynamic.NSTrackingAreaOptions.mouseEnteredAndExited.asObject as? UInt,
|
||||
let activeAlways = Dynamic.NSTrackingAreaOptions.activeAlways.asObject as? UInt
|
||||
else {
|
||||
logger.error("Failed to get tracking area options")
|
||||
return
|
||||
}
|
||||
let options = mouseEnteredAndExited | activeAlways
|
||||
|
||||
let trackingArea = Dynamic.NSTrackingArea.alloc()
|
||||
.initWithRect(trackingRect, options: options, owner: button, userInfo: nil)
|
||||
|
||||
button.addTrackingArea(trackingArea)
|
||||
}
|
||||
}
|
||||
// Dynamic framework methods removed - not available without proper package integration
|
||||
|
||||
private func setupWindowObservers() {
|
||||
// Clean up existing observers
|
||||
if let observer = windowResizeObserver {
|
||||
NotificationCenter.default.removeObserver(observer)
|
||||
}
|
||||
if let observer = windowDidBecomeKeyObserver {
|
||||
NotificationCenter.default.removeObserver(observer)
|
||||
}
|
||||
|
||||
// Observe window resize events
|
||||
windowResizeObserver = NotificationCenter.default.addObserver(
|
||||
forName: NSNotification.Name("NSWindowDidResizeNotification"),
|
||||
object: nil,
|
||||
queue: .main
|
||||
) { [weak self] notification in
|
||||
guard let self,
|
||||
self.windowStyle == .inline,
|
||||
let window = self.window,
|
||||
let notificationWindow = notification.object as? NSObject,
|
||||
let currentNSWindow = window.nsWindow,
|
||||
notificationWindow == currentNSWindow else { return }
|
||||
|
||||
// Reapply inline style on resize
|
||||
DispatchQueue.main.async {
|
||||
self.applyWindowStyle(.inline)
|
||||
}
|
||||
}
|
||||
|
||||
// Observe window becoming key
|
||||
windowDidBecomeKeyObserver = NotificationCenter.default.addObserver(
|
||||
forName: UIWindow.didBecomeKeyNotification,
|
||||
object: window,
|
||||
queue: .main
|
||||
) { [weak self] _ in
|
||||
guard let self,
|
||||
self.windowStyle == .inline else { return }
|
||||
|
||||
// Reapply inline style when window becomes key
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
self.applyWindowStyle(.inline)
|
||||
}
|
||||
}
|
||||
|
||||
// Also observe the NS notification for tracking area updates
|
||||
NotificationCenter.default.addObserver(
|
||||
forName: NSNotification.Name("NSViewDidUpdateTrackingAreasNotification"),
|
||||
object: nil,
|
||||
queue: .main
|
||||
) { [weak self] _ in
|
||||
guard let self,
|
||||
self.windowStyle == .inline else { return }
|
||||
|
||||
// Reposition if needed
|
||||
if let window = self.window,
|
||||
let nsWindow = window.nsWindow
|
||||
{
|
||||
self.repositionTrafficLights(Dynamic(nsWindow), window: window)
|
||||
}
|
||||
}
|
||||
// Window observation disabled - Dynamic framework not available
|
||||
logger.info("Window observation disabled - Dynamic framework not available")
|
||||
}
|
||||
|
||||
deinit {
|
||||
if let observer = windowResizeObserver {
|
||||
NotificationCenter.default.removeObserver(observer)
|
||||
}
|
||||
if let observer = windowDidBecomeKeyObserver {
|
||||
NotificationCenter.default.removeObserver(observer)
|
||||
}
|
||||
// No observers to clean up since Dynamic framework is not available
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -274,7 +95,7 @@ import SwiftUI
|
|||
/// Configures window appearance when the view appears.
|
||||
struct MacCatalystWindowStyle: ViewModifier {
|
||||
let style: MacWindowStyle
|
||||
@StateObject private var windowManager = MacCatalystWindowManager.shared
|
||||
@State private var windowManager = MacCatalystWindowManager.shared
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ struct EnhancedConnectionView: View {
|
|||
@State private var showingProfileEditor = false
|
||||
|
||||
#if targetEnvironment(macCatalyst)
|
||||
@StateObject private var windowManager = MacCatalystWindowManager.shared
|
||||
@State private var windowManager = MacCatalystWindowManager.shared
|
||||
#endif
|
||||
|
||||
var body: some View {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ struct ServerListView: View {
|
|||
}
|
||||
|
||||
#if targetEnvironment(macCatalyst)
|
||||
@StateObject private var windowManager = MacCatalystWindowManager.shared
|
||||
@State private var windowManager = MacCatalystWindowManager.shared
|
||||
#endif
|
||||
|
||||
var body: some View {
|
||||
|
|
|
|||
|
|
@ -259,7 +259,7 @@ struct AdvancedSettingsView: View {
|
|||
#if targetEnvironment(macCatalyst)
|
||||
@AppStorage("macWindowStyle")
|
||||
private var macWindowStyleRaw = "standard"
|
||||
@StateObject private var windowManager = MacCatalystWindowManager.shared
|
||||
@State private var windowManager = MacCatalystWindowManager.shared
|
||||
|
||||
private var macWindowStyle: MacWindowStyle {
|
||||
macWindowStyleRaw == "inline" ? .inline : .standard
|
||||
|
|
|
|||
Loading…
Reference in a new issue