Files
tvbox-Swift/tvbox/ViewModels/SearchViewModel.swift
T
2026-03-01 22:57:20 +08:00

105 lines
3.5 KiB
Swift

import Foundation
import SwiftUI
/// 搜索 ViewModel
@MainActor
class SearchViewModel: ObservableObject {
/// 搜索关键词输入。
@Published var keyword: String = ""
/// 当前结果列表。
@Published var results: [Movie.Video] = []
/// 搜索加载状态,用于控制进度指示器。
@Published var isSearching = false
/// 本地搜索历史(最近在前)。
@Published var searchHistory: [String] = []
/// 搜索失败或空结果提示。
@Published var errorMessage: String?
/// 源数据服务(负责多源并发搜索)。
private let sourceService = SourceService.shared
/// 搜索请求序号(用于丢弃过期异步结果)。
private var latestSearchRequestId: UUID = UUID()
/// 初始化时同步加载本地历史记录,确保搜索页首次渲染即可展示。
init() {
loadSearchHistory()
}
/// 执行搜索
func search() async {
let trimmed = keyword.trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmed.isEmpty else { return }
let requestId = UUID()
latestSearchRequestId = requestId
isSearching = true
errorMessage = nil
results = []
// 搜索一旦触发就先落历史,保持行为与移动端常见搜索体验一致。
addToHistory(trimmed)
// 走多源并发搜索,返回聚合后的影片列表。
let videos = await sourceService.searchAll(keyword: trimmed)
guard requestId == latestSearchRequestId else { return }
self.results = videos
if videos.isEmpty {
errorMessage = "未找到相关内容"
}
isSearching = false
}
/// 在指定源搜索
func searchInSource(_ source: SourceBean) async {
let trimmed = keyword.trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmed.isEmpty else { return }
let requestId = UUID()
latestSearchRequestId = requestId
isSearching = true
errorMessage = nil
do {
let videos = try await sourceService.search(sourceBean: source, keyword: trimmed)
guard requestId == latestSearchRequestId else { return }
self.results = videos
} catch {
guard requestId == latestSearchRequestId else { return }
errorMessage = error.localizedDescription
}
isSearching = false
}
// MARK: - 搜索历史
/// 从本地读取历史。
private func loadSearchHistory() {
searchHistory = UserDefaults.standard.stringArray(forKey: HawkConfig.SEARCH_HISTORY) ?? []
}
/// 新增历史项并去重,最多保留 20 条。
private func addToHistory(_ keyword: String) {
searchHistory.removeAll { $0 == keyword }
searchHistory.insert(keyword, at: 0)
if searchHistory.count > 20 {
searchHistory = Array(searchHistory.prefix(20))
}
UserDefaults.standard.set(searchHistory, forKey: HawkConfig.SEARCH_HISTORY)
}
/// 清空历史。
func clearHistory() {
searchHistory = []
UserDefaults.standard.removeObject(forKey: HawkConfig.SEARCH_HISTORY)
}
/// 删除单条历史。
func removeFromHistory(_ keyword: String) {
searchHistory.removeAll { $0 == keyword }
UserDefaults.standard.set(searchHistory, forKey: HawkConfig.SEARCH_HISTORY)
}
}