Actually commit the oAITests target wiring in project.pbxproj

The oAITests PBXNativeTarget (PBXContainerItemProxy, PBXTargetDependency,
XCBuildConfiguration with TEST_HOST/BUNDLE_LOADER, the "recommended
settings" changes accepted when the target was created -- DEAD_CODE_STRIPPING,
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED, STRING_CATALOG_GENERATE_SYMBOLS,
DEVELOPMENT_TEAM moved to project-level inheritance) was somehow never
actually staged in the very first "Add real oAITests target" commit
(8c7fb59) despite every xcodebuild test run since then depending on it
being present on disk. Every subsequent commit this session only staged
specific file paths (never oAI.xcodeproj again), so the gap went
unnoticed until a full `git status` review here.

Without this, anyone else pulling the branch (or a truly clean checkout
on this machine) would have all the .swift test files but no target to
compile them into -- xcodebuild test would fail to find oAITests at all.
Confirmed the diff is exactly the expected target-wiring content, nothing
unrelated or corrupted, before committing.
This commit is contained in:
2026-07-23 14:19:53 +02:00
parent a053d4a983
commit 02bf73fec6
9 changed files with 464 additions and 72 deletions
+68 -27
View File
@@ -206,37 +206,68 @@ final class BackupService {
log.debug("Pushed \(payload.ids.count) favorite model(s) to iCloud")
}
/// Outcome of reconciling local favorites against the iCloud copy. Pure result type --
/// see `decideFavoritesSync` for the actual decision logic.
enum FavoritesSyncDecision: Equatable {
case applyRemote(ids: Set<String>, updatedAt: String)
case pushLocal
case mergeAndPush(ids: Set<String>, updatedAt: String)
case noop
}
/// Last-write-wins reconciliation, pulled out of `syncFavoritesOnLaunch` so the branching
/// logic (5 cases: remote absent / remote newer / local newer / tied-but-different /
/// tied-and-identical) can be tested without touching iCloud Drive or SettingsService.
static func decideFavoritesSync(
localIds: Set<String>,
localUpdatedAt: String,
remote: FavoritesPayload?,
now: Date
) -> FavoritesSyncDecision {
guard let remote else {
return .pushLocal
}
if remote.updatedAt > localUpdatedAt {
return .applyRemote(ids: Set(remote.ids), updatedAt: remote.updatedAt)
} else if localUpdatedAt > remote.updatedAt {
return .pushLocal
} else if Set(remote.ids) != localIds {
// Tied timestamps (both empty is the common case: two machines that already had
// favorites before this sync feature existed, neither ever bumped the timestamp).
// Union rather than silently dropping one side's favorites.
let merged = localIds.union(remote.ids)
return .mergeAndPush(ids: merged, updatedAt: ISO8601DateFormatter().string(from: now))
}
return .noop
}
/// Reconcile local favorites with the iCloud copy using last-write-wins (by timestamp).
/// Call on launch (and optionally on app-become-active) to pick up changes from other machines.
func syncFavoritesOnLaunch() async {
let settings = SettingsService.shared
let fileURL = favoritesFileURL()
guard let data = try? Data(contentsOf: fileURL),
let remote = try? JSONDecoder().decode(FavoritesPayload.self, from: data) else {
// No remote copy yet push local state (may be empty, that's fine).
await pushFavorites()
return
}
let localUpdatedAt = settings.favoriteModelsUpdatedAt
let remote: FavoritesPayload? = {
guard let data = try? Data(contentsOf: fileURL) else { return nil }
return try? JSONDecoder().decode(FavoritesPayload.self, from: data)
}()
let localIds = settings.favoriteModelIds
if remote.updatedAt > localUpdatedAt {
settings.favoriteModelIds = Set(remote.ids)
settings.favoriteModelsUpdatedAt = remote.updatedAt
log.info("Applied \(remote.ids.count) favorite model(s) from iCloud (remote was newer)")
} else if localUpdatedAt > remote.updatedAt {
switch Self.decideFavoritesSync(localIds: localIds, localUpdatedAt: settings.favoriteModelsUpdatedAt, remote: remote, now: Date()) {
case .pushLocal:
await pushFavorites()
} else if Set(remote.ids) != localIds {
// Tied timestamps (both empty is the common case: two machines that already had
// favorites before this sync feature existed, neither ever bumped the timestamp).
// Union rather than silently dropping one side's favorites.
settings.favoriteModelIds = localIds.union(remote.ids)
settings.favoriteModelsUpdatedAt = ISO8601DateFormatter().string(from: Date())
case .applyRemote(let ids, let updatedAt):
settings.favoriteModelIds = ids
settings.favoriteModelsUpdatedAt = updatedAt
log.info("Applied \(ids.count) favorite model(s) from iCloud (remote was newer)")
case .mergeAndPush(let ids, let updatedAt):
settings.favoriteModelIds = ids
settings.favoriteModelsUpdatedAt = updatedAt
await pushFavorites()
log.info("Merged favorites from iCloud (tied timestamps) — union of \(localIds.count) local + \(remote.ids.count) remote")
log.info("Merged favorites from iCloud (tied timestamps) — union of \(localIds.count) local + \(remote?.ids.count ?? 0) remote")
case .noop:
break
}
// Equal timestamps and identical sets: already in sync, nothing to do.
}
// MARK: - Automatic Backup
@@ -252,17 +283,27 @@ final class BackupService {
}
}
func checkAndPerformAutoBackupIfDue() async {
let frequency = SettingsService.shared.autoBackupFrequency
/// Whether an automatic backup should run now, given the chosen frequency and the last
/// known backup time. Pulled out of `checkAndPerformAutoBackupIfDue` for testability --
/// "manual" is never due, a missing last-backup-date is always due, otherwise it's due
/// once the elapsed time reaches the frequency's interval.
static func isBackupDue(frequency: String, lastBackupDate: Date?, now: Date) -> Bool {
let interval: TimeInterval
switch frequency {
case "daily": interval = Self.dailyInterval
case "weekly": interval = Self.weeklyInterval
default: return // "manual" user triggers backups by hand
case "daily": interval = dailyInterval
case "weekly": interval = weeklyInterval
default: return false // "manual" user triggers backups by hand
}
guard let last = lastBackupDate else { return true }
return now.timeIntervalSince(last) >= interval
}
func checkAndPerformAutoBackupIfDue() async {
let frequency = SettingsService.shared.autoBackupFrequency
guard frequency == "daily" || frequency == "weekly" else { return }
checkForExistingBackup()
if let last = lastBackupDate, Date().timeIntervalSince(last) < interval {
guard Self.isBackupDue(frequency: frequency, lastBackupDate: lastBackupDate, now: Date()) else {
return // not due yet
}