vibetunnel/ios/docs/modern-swift.md
Peter Steinberger 8ddde1d6c2 feat(ios): Fix iOS build and integrate SwiftTerm for terminal emulation
Major improvements to the iOS VibeTunnel app:

SwiftTerm Integration:
- Properly integrated SwiftTerm 1.2.5 for terminal emulation
- Fixed naming conflicts and API compatibility issues
- Added proper actor isolation with @preconcurrency
- Terminal data now feeds correctly via SSE streams

Session Management:
- Fixed session model to match Go server's JSON response format
- Added CodingKeys mapping for field name differences (cmdline, cwd, etc.)
- Support for "starting" session status
- Enhanced date parsing for both ISO8601 and RFC3339 formats

Working Directory Fixes:
- Changed default working directory from iOS sandbox to server paths
- Now uses ~/ as default (matching web frontend)
- Added common server directories as quick options
- Server expands ~ to actual home directory

Terminal Streaming:
- Implemented Asciinema cast v2 format parsing
- SSE client properly handles output, resize, and exit events
- Added terminal snapshot loading for existing sessions
- Fixed special key handling with proper ANSI escape sequences

UI Improvements:
- Updated session list to grid layout (2 columns)
- Added session count and "Kill All" functionality
- Redesigned session cards with inline kill/clean buttons
- Shows command and working directory in session preview

Error Handling:
- Added comprehensive logging for debugging
- Enhanced error messages for better user experience
- Detailed API request/response logging
- Network error descriptions (connection refused, timeout, etc.)

Server Configuration:
- Set default server address to 127.0.0.1:4020
- Sessions now created with spawn_terminal: false for PTY mode
- Both web and iOS clients can share terminal sessions

The iOS app now provides a full terminal experience using SwiftTerm,
mirroring the web frontend's functionality with proper server integration.
2025-06-20 05:45:48 +02:00

4.4 KiB

Modern Swift Development

Write idiomatic SwiftUI code following Apple's latest architectural recommendations and best practices.

Core Philosophy

  • SwiftUI is the default UI paradigm for Apple platforms - embrace its declarative nature
  • Avoid legacy UIKit patterns and unnecessary abstractions
  • Focus on simplicity, clarity, and native data flow
  • Let SwiftUI handle the complexity - don't fight the framework

Architecture Guidelines

1. Embrace Native State Management

Use SwiftUI's built-in property wrappers appropriately:

  • @State - Local, ephemeral view state
  • @Binding - Two-way data flow between views
  • @Observable - Shared state (iOS 17+)
  • @ObservableObject - Legacy shared state (pre-iOS 17)
  • @Environment - Dependency injection for app-wide concerns

2. State Ownership Principles

  • Views own their local state unless sharing is required
  • State flows down, actions flow up
  • Keep state as close to where it's used as possible
  • Extract shared state only when multiple views need it

3. Modern Async Patterns

  • Use async/await as the default for asynchronous operations
  • Leverage .task modifier for lifecycle-aware async work
  • Avoid Combine unless absolutely necessary
  • Handle errors gracefully with try/catch

4. View Composition

  • Build UI with small, focused views
  • Extract reusable components naturally
  • Use view modifiers to encapsulate common styling
  • Prefer composition over inheritance

5. Code Organization

  • Organize by feature, not by type (avoid Views/, Models/, ViewModels/ folders)
  • Keep related code together in the same file when appropriate
  • Use extensions to organize large files
  • Follow Swift naming conventions consistently

Implementation Patterns

Simple State Example

struct CounterView: View {
    @State private var count = 0
    
    var body: some View {
        VStack {
            Text("Count: \(count)")
            Button("Increment") { 
                count += 1 
            }
        }
    }
}

Shared State with @Observable

@Observable
class UserSession {
    var isAuthenticated = false
    var currentUser: User?
    
    func signIn(user: User) {
        currentUser = user
        isAuthenticated = true
    }
}

struct MyApp: App {
    @State private var session = UserSession()
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(session)
        }
    }
}

Async Data Loading

struct ProfileView: View {
    @State private var profile: Profile?
    @State private var isLoading = false
    @State private var error: Error?
    
    var body: some View {
        Group {
            if isLoading {
                ProgressView()
            } else if let profile {
                ProfileContent(profile: profile)
            } else if let error {
                ErrorView(error: error)
            }
        }
        .task {
            await loadProfile()
        }
    }
    
    private func loadProfile() async {
        isLoading = true
        defer { isLoading = false }
        
        do {
            profile = try await ProfileService.fetch()
        } catch {
            self.error = error
        }
    }
}

Best Practices

DO:

  • Write self-contained views when possible
  • Use property wrappers as intended by Apple
  • Test logic in isolation, preview UI visually
  • Handle loading and error states explicitly
  • Keep views focused on presentation
  • Use Swift's type system for safety

DON'T:

  • Create ViewModels for every view
  • Move state out of views unnecessarily
  • Add abstraction layers without clear benefit
  • Use Combine for simple async operations
  • Fight SwiftUI's update mechanism
  • Overcomplicate simple features

Testing Strategy

  • Unit test business logic and data transformations
  • Use SwiftUI Previews for visual testing
  • Test @Observable classes independently
  • Keep tests simple and focused
  • Don't sacrifice code clarity for testability

Modern Swift Features

  • Use Swift Concurrency (async/await, actors)
  • Leverage Swift 6 data race safety when available
  • Utilize property wrappers effectively
  • Embrace value types where appropriate
  • Use protocols for abstraction, not just for testing

Summary

Write SwiftUI code that looks and feels like SwiftUI. The framework has matured significantly - trust its patterns and tools. Focus on solving user problems rather than implementing architectural patterns from other platforms.