Files
GHOSTWIRE/ios/GHOSTWIRE/GhostwireApp.swift
T
Daniel Redetzke 704859a369 Add native iOS app with App Store preparation
iOS app (ios/, SwiftUI, iOS 17+) in the web UI's styling with the Kamon
logo. It covers everything the web interface does except password, API
tokens and backups: dashboard, peers with search and filter, peer detail
with traffic charts, add/edit peers, one-time config with QR code and
share sheet, server settings with apply bar, app settings, data
retention and log viewer.

- Pairing by QR code or pasted pairing code; token kept in the keychain;
  self-signed certificates are pinned by SHA-256 fingerprint.
- Colour providers and logo drawing are nonisolated: SwiftUI's background
  renderer calls them, and main-actor closures crashed there when the
  camera scanner was open.
- App Store: version 1.0, export compliance, privacy manifest, app icon,
  release.sh (archive and upload), listing text, review notes and 6.9"
  screenshots in ios/AppStore.

Server:
- Full-access API tokens may use settings, logs and restart; password,
  tokens, backup/restore and the admin username stay admin-only.
- Web pairing dialog gains "Copy pairing code".
- The development simulator reports health checks in Linux wording.
2026-10-03 18:34:41 +03:00

66 lines
1.8 KiB
Swift

import SwiftUI
@main
struct GhostwireApp: App {
@State private var session = AppSession()
var body: some Scene {
WindowGroup {
RootView()
.environment(session)
.tint(Color.gwAccent)
}
}
}
struct RootView: View {
@Environment(AppSession.self) private var session
var body: some View {
@Bindable var session = session
Group {
if session.api == nil {
PairingView()
} else {
MainTabView()
}
}
.alert("GHOSTWIRE", isPresented: Binding(get: { session.alert != nil }, set: { if !$0 { session.alert = nil } })) {
Button("OK", role: .cancel) {}
} message: {
Text(session.alert ?? "")
}
}
}
struct MainTabView: View {
enum Tab: String { case dashboard, peers, server, settings }
@Environment(AppSession.self) private var session
@State private var tab: Tab = {
#if DEBUG
// Development: `-tab peers` opens a tab directly.
if let t = UserDefaults.standard.string(forKey: "tab"), let tab = Tab(rawValue: t) { return tab }
#endif
return .dashboard
}()
var body: some View {
TabView(selection: $tab) {
DashboardView()
.tabItem { Label("Dashboard", systemImage: "square.grid.2x2") }
.tag(Tab.dashboard)
PeersView()
.tabItem { Label("Peers", systemImage: "person.2") }
.tag(Tab.peers)
ServerView()
.tabItem { Label("Server", systemImage: "server.rack") }
.tag(Tab.server)
SettingsView()
.tabItem { Label("Settings", systemImage: "slider.horizontal.3") }
.tag(Tab.settings)
}
.task { await session.loadMe() }
}
}