chore: scaffold Voice iOS app (Phase 0)

XcodeGen project, SwiftUI iOS 17 target dev.shane.voice, SPM
livekit-client-swift 2.16.0, mic usage + ATS LAN exception in
Info.plist, placeholder conversation screen and Settings with
server URL field.
This commit is contained in:
snowspeeder committed 2026-08-29 17:15:27 -04:00
commit 297fbaa19c
17 files changed
+1056

No files matched your search

+41
View File
@@ -0,0 +1,41 @@
import Foundation
import Combine
final class SettingsStore: ObservableObject {
@Published var serverURL: String {
didSet { UserDefaults.standard.set(serverURL, forKey: Keys.serverURL) }
}
@Published var voiceID: String {
didSet { UserDefaults.standard.set(voiceID, forKey: Keys.voiceID) }
}
@Published var modelID: String? {
didSet { UserDefaults.standard.set(modelID, forKey: Keys.modelID) }
}
@Published var textMode: Bool {
didSet { UserDefaults.standard.set(textMode, forKey: Keys.textMode) }
}
@Published var volume: Double {
didSet { UserDefaults.standard.set(volume, forKey: Keys.volume) }
}
private enum Keys {
static let serverURL = "serverURL"
static let voiceID = "voiceID"
static let modelID = "modelID"
static let textMode = "textMode"
static let volume = "volume"
}
init() {
let d = UserDefaults.standard
serverURL = d.string(forKey: Keys.serverURL) ?? "https://192.168.86.2:9178"
voiceID = d.string(forKey: Keys.voiceID) ?? ""
modelID = d.string(forKey: Keys.modelID)
textMode = d.bool(forKey: Keys.textMode)
volume = d.object(forKey: Keys.volume) as? Double ?? 0.8
}
var trimmedServerURL: String {
serverURL.trimmingCharacters(in: .whitespacesAndNewlines)
}
}
+13
View File
@@ -0,0 +1,13 @@
import SwiftUI
@main
struct VoiceApp: App {
@StateObject private var settings = SettingsStore()
var body: some Scene {
WindowGroup {
ConversationView(settings: settings)
.preferredColorScheme(.dark)
}
}
}
@@ -0,0 +1,36 @@
import SwiftUI
struct ConversationView: View {
@ObservedObject var settings: SettingsStore
var body: some View {
NavigationStack {
VStack(spacing: 24) {
Spacer()
Circle()
.fill(.blue.gradient)
.frame(width: 96, height: 96)
Text("Hope Voice")
.font(.title.bold())
Text(settings.trimmedServerURL)
.font(.footnote)
.foregroundStyle(.secondary)
Spacer()
}
.navigationTitle("Voice")
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
NavigationLink {
SettingsView(settings: settings)
} label: {
Image(systemName: "gearshape")
}
}
}
}
}
}
#Preview {
ConversationView(settings: SettingsStore())
}
@@ -0,0 +1,28 @@
import SwiftUI
struct SettingsView: View {
@ObservedObject var settings: SettingsStore
@State private var urlText: String = ""
var body: some View {
Form {
Section("Server") {
TextField("https://192.168.86.2:9178", text: $urlText)
.keyboardType(.URL)
.autocorrectionDisabled()
.onAppear { urlText = settings.serverURL }
.onChange(of: urlText) { _, newValue in
settings.serverURL = newValue
}
}
Section("Interaction") {
Toggle("Text mode", isOn: $settings.textMode)
}
}
.navigationTitle("Settings")
}
}
#Preview {
SettingsView(settings: SettingsStore())
}