iOS App Architecture Review
Review code changes in app/FlyFunEuroAIP/ for compliance with the architecture defined in designs/IOS_APP_DESIGN.md.
Architecture Rules
- •
RZFlight Model Reuse (CRITICAL):
- •Use
RZFlight.Airport,RZFlight.Runway,RZFlight.Procedure,RZFlight.AIPEntrydirectly - •No duplicate models
- •Query through
KnownAirports - •Extend RZFlight library when functionality is missing
- •Use
- •
Composed AppState:
- •Single source of truth with domain objects
- •Domains:
AirportDomain,ChatDomain,NavigationDomain,SystemDomain,SettingsDomain - •Each domain ~200-400 lines
- •Cross-domain coordination via callbacks
- •Inject via
@Environment(\.appState)
- •
No ViewModels:
- •Domains replace ViewModels
- •No standalone
*ViewModel.swiftfiles - •ViewCoordinators are structs with computed properties only
- •
Repository Pattern:
- •
AirportRepositoryProtocoldefines unified API - •
LocalAirportDataSourceusesKnownAirports - •Repository returns
RZFlight.Airportdirectly
- •
- •
FilterConfig is Pure Data:
- •Simple
Codablestruct - •No
apply(to:db:)method - •Filtering logic lives in repository
- •Simple
- •
API Adapters:
- •API response models are internal only
- •
APIAirportAdapter.toRZFlight()converts immediately - •Never expose
APIAirportoutside adapters
- •
Region-Based Map Loading:
- •Use
airportsInRegion(boundingBox:filters:limit:) - •Debounce 300ms
- •Limit 500 markers
- •Prefetch with 1.3x padding
- •Use
- •
File-Based Caching:
- •Simple JSON files
- •No SwiftData
- •Cache in
Caches/directory
- •
Modern Swift Only (iOS 18.0+/macOS 15.0+):
- •
@Observablemacro (noObservableObject) - •
@Environmentinjection (no@EnvironmentObject) - •
async/awaiteverywhere (no Combine) - •Modern MapKit with
Map { }builder
- •
- •
DB is Canonical Source:
- •Local SQLite is truth
- •API is read-only view
- •Full DB replacement for sync
Red Flags
Flag these violations immediately:
- •Duplicate models: Creating
Airport,Runwayin app instead of using RZFlight - •ViewModel files: Any
*ViewModel.swiftfile - •God-class AppState: Single file >500 lines without domain composition
- •Direct SQL queries: Using
FMDatabasedirectly instead ofKnownAirports - •FilterConfig with DB logic:
apply(to:db:)method - •Exposed API models:
APIAirportused outside adapter files - •SwiftData usage: Any
@Modelor SwiftData imports - •ObservableObject: Using instead of
@Observable - •Combine imports: Using for state management
- •All airports loaded: Loading all 10K+ airports at once for map
Review Process
- •Analyze changed files in
app/FlyFunEuroAIP/ - •Check imports - should see
import RZFlight, noimport Combine - •Verify model usage - search for
RZFlight.Airportvs localAirport - •Check state management - domains composed in AppState?
- •Verify repository pattern - data access through repository?
- •Check UI patterns - adaptive layouts, environment injection?
- •Identify violations with file paths and line numbers
- •Suggest fixes with code examples
Output Format
APPROVED:
- •
file:line- Explanation of why it's correct
VIOLATION:
- •
file:line- Description - •Problem: Why it violates architecture
- •Fix: Suggested corrected implementation
- •Impact: What breaks (performance, maintainability, etc.)
Key Files
Core Architecture
- •
App/State/AppState.swift- Composed domains? - •
App/State/Domains/*.swift- Each domain ~200-400 lines? - •
App/Data/Repositories/AirportRepository.swift- Returns RZFlight types?
Model Usage
- •
App/Models/*.swift- Should be minimal (app-specific only) - •Any
struct Airport- Should NOT exist (use RZFlight.Airport)
UI Layer
- •
UserInterface/Views/*.swift- Uses@Environment(\.appState)? - •
UserInterface/Views/Map/*.swift- Region-based loading?
Approved Patterns
RZFlight Model Usage:
swift
// GOOD
import RZFlight
let airport: Airport // This is RZFlight.Airport
// BAD
struct AppAirport { let icao: String; let name: String }
Composed AppState:
swift
// GOOD
@Observable @MainActor
final class AppState {
let airports: AirportDomain
let chat: ChatDomain
let navigation: NavigationDomain
}
Environment Injection:
swift
// GOOD
struct AirportMapView: View {
@Environment(\.appState) private var state
}
// BAD
@StateObject private var viewModel = AirportMapViewModel()
Region-Based Loading:
swift
// GOOD
func onRegionChange(_ region: MKCoordinateRegion) {
regionUpdateTask?.cancel()
regionUpdateTask = Task {
try? await Task.sleep(for: .milliseconds(300))
airports = try await repository.airportsInRegion(
boundingBox: region.paddedBy(factor: 1.3).boundingBox,
filters: filters, limit: 500
)
}
}
// BAD
airports = try await repository.allAirports() // 10K+ airports!
Reference
See designs/IOS_APP_DESIGN.md for full design details.