ios: initial commit

This commit is contained in:
2026-02-20 00:09:02 -08:00
parent b91b03b74f
commit c47646a48c
24 changed files with 3406 additions and 19 deletions

View File

@@ -0,0 +1,73 @@
import Foundation
import Observation
@MainActor
@Observable
final class SybilSettingsStore {
private enum Keys {
static let apiBaseURL = "sybil.ios.apiBaseURL"
static let adminToken = "sybil.ios.adminToken"
static let preferredProvider = "sybil.ios.preferredProvider"
static let preferredOpenAIModel = "sybil.ios.preferredOpenAIModel"
static let preferredAnthropicModel = "sybil.ios.preferredAnthropicModel"
static let preferredXAIModel = "sybil.ios.preferredXAIModel"
}
private let defaults: UserDefaults
var apiBaseURL: String
var adminToken: String
var preferredProvider: Provider
var preferredModelByProvider: [Provider: String]
init(defaults: UserDefaults = .standard) {
self.defaults = defaults
let storedBaseURL = defaults.string(forKey: Keys.apiBaseURL)?.trimmingCharacters(in: .whitespacesAndNewlines)
let fallbackBaseURL = "http://127.0.0.1:8787/api"
self.apiBaseURL = storedBaseURL?.isEmpty == false ? storedBaseURL! : fallbackBaseURL
self.adminToken = defaults.string(forKey: Keys.adminToken) ?? ""
let provider = defaults.string(forKey: Keys.preferredProvider).flatMap(Provider.init(rawValue:)) ?? .openai
self.preferredProvider = provider
self.preferredModelByProvider = [
.openai: defaults.string(forKey: Keys.preferredOpenAIModel) ?? "gpt-4.1-mini",
.anthropic: defaults.string(forKey: Keys.preferredAnthropicModel) ?? "claude-3-5-sonnet-latest",
.xai: defaults.string(forKey: Keys.preferredXAIModel) ?? "grok-3-mini"
]
}
func persist() {
defaults.set(apiBaseURL.trimmingCharacters(in: .whitespacesAndNewlines), forKey: Keys.apiBaseURL)
let trimmedToken = adminToken.trimmingCharacters(in: .whitespacesAndNewlines)
if trimmedToken.isEmpty {
defaults.removeObject(forKey: Keys.adminToken)
} else {
defaults.set(trimmedToken, forKey: Keys.adminToken)
}
defaults.set(preferredProvider.rawValue, forKey: Keys.preferredProvider)
defaults.set(preferredModelByProvider[.openai], forKey: Keys.preferredOpenAIModel)
defaults.set(preferredModelByProvider[.anthropic], forKey: Keys.preferredAnthropicModel)
defaults.set(preferredModelByProvider[.xai], forKey: Keys.preferredXAIModel)
}
var trimmedTokenOrNil: String? {
let value = adminToken.trimmingCharacters(in: .whitespacesAndNewlines)
return value.isEmpty ? nil : value
}
var normalizedAPIBaseURL: URL? {
var raw = apiBaseURL.trimmingCharacters(in: .whitespacesAndNewlines)
guard !raw.isEmpty else { return nil }
if raw.hasSuffix("/") {
raw.removeLast()
}
return URL(string: raw)
}
}