feat: 优化视图组件与数据迁移逻辑
- 移除DataMigrationManager类,简化数据迁移逻辑。 - 在FeedListView和MeView中新增图片预览功能,提升用户体验。 - 更新OptimizedDynamicCardView以支持图片点击回调,增强交互性。 - 新增PreviewItem结构体以管理图片预览状态,提升代码可读性与维护性。 - 清理AppDelegate中的冗余代码,优化启动流程。
This commit is contained in:
@@ -1,357 +0,0 @@
|
||||
import Foundation
|
||||
|
||||
/// 数据迁移管理器
|
||||
///
|
||||
/// 负责将旧版本的 UserDefaults 存储数据迁移到新的 Keychain 存储方案。
|
||||
/// 确保用户升级应用后无需重新登录。
|
||||
///
|
||||
/// 迁移策略:
|
||||
/// 1. 检测旧数据是否存在
|
||||
/// 2. 迁移到 Keychain
|
||||
/// 3. 验证迁移结果
|
||||
/// 4. 清理旧数据
|
||||
@MainActor
|
||||
final class DataMigrationManager {
|
||||
|
||||
// MARK: - 单例
|
||||
static let shared = DataMigrationManager()
|
||||
private init() {}
|
||||
|
||||
// MARK: - 迁移状态
|
||||
private let migrationCompleteKey = "keychain_migration_completed_v1"
|
||||
|
||||
// MARK: - 旧版本存储键
|
||||
private enum LegacyStorageKeys {
|
||||
static let userId = "user_id"
|
||||
static let accessToken = "access_token"
|
||||
static let userInfo = "user_info"
|
||||
static let accountModel = "account_model"
|
||||
static let appLanguage = "AppLanguage"
|
||||
}
|
||||
|
||||
// MARK: - 迁移结果
|
||||
enum MigrationResult {
|
||||
case completed // 迁移完成
|
||||
case alreadyMigrated // 已经迁移过
|
||||
case noDataToMigrate // 没有需要迁移的数据
|
||||
case failed(Error) // 迁移失败
|
||||
|
||||
var description: String {
|
||||
switch self {
|
||||
case .completed:
|
||||
return "数据迁移完成"
|
||||
case .alreadyMigrated:
|
||||
return "数据已经迁移过"
|
||||
case .noDataToMigrate:
|
||||
return "没有需要迁移的数据"
|
||||
case .failed(let error):
|
||||
return "迁移失败: \(error.localizedDescription)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - 公共方法
|
||||
|
||||
/// 执行数据迁移
|
||||
/// - Returns: 迁移结果
|
||||
func performMigration() -> MigrationResult {
|
||||
debugInfoSync("🔄 开始检查数据迁移...")
|
||||
|
||||
// 检查是否已经迁移过
|
||||
if isMigrationCompleted() {
|
||||
debugInfoSync("✅ 数据已经迁移过,跳过迁移")
|
||||
return .alreadyMigrated
|
||||
}
|
||||
|
||||
// 检查是否有需要迁移的数据
|
||||
let legacyData = collectLegacyData()
|
||||
if legacyData.isEmpty {
|
||||
debugInfoSync("ℹ️ 没有发现需要迁移的数据")
|
||||
markMigrationCompleted()
|
||||
return .noDataToMigrate
|
||||
}
|
||||
|
||||
debugInfoSync("📦 发现需要迁移的数据: \(legacyData.keys.joined(separator: ", "))")
|
||||
|
||||
do {
|
||||
// 执行迁移
|
||||
try migrateToKeychain(legacyData)
|
||||
|
||||
// 验证迁移结果
|
||||
try verifyMigration(legacyData)
|
||||
|
||||
// 清理旧数据
|
||||
cleanupLegacyData(legacyData.keys)
|
||||
|
||||
// 标记迁移完成
|
||||
markMigrationCompleted()
|
||||
|
||||
debugInfoSync("✅ 数据迁移完成")
|
||||
return .completed
|
||||
|
||||
} catch {
|
||||
debugErrorSync("❌ 数据迁移失败: \(error)")
|
||||
return .failed(error)
|
||||
}
|
||||
}
|
||||
|
||||
/// 强制重新迁移(用于测试或修复)
|
||||
func forceMigration() -> MigrationResult {
|
||||
resetMigrationStatus()
|
||||
return performMigration()
|
||||
}
|
||||
|
||||
// MARK: - 私有方法
|
||||
|
||||
/// 检查迁移是否已完成
|
||||
private func isMigrationCompleted() -> Bool {
|
||||
return UserDefaults.standard.bool(forKey: migrationCompleteKey)
|
||||
}
|
||||
|
||||
/// 标记迁移完成
|
||||
private func markMigrationCompleted() {
|
||||
UserDefaults.standard.set(true, forKey: migrationCompleteKey)
|
||||
UserDefaults.standard.synchronize()
|
||||
}
|
||||
|
||||
/// 重置迁移状态
|
||||
private func resetMigrationStatus() {
|
||||
UserDefaults.standard.removeObject(forKey: migrationCompleteKey)
|
||||
UserDefaults.standard.synchronize()
|
||||
}
|
||||
|
||||
/// 收集旧版本数据
|
||||
private func collectLegacyData() -> [String: Any] {
|
||||
let userDefaults = UserDefaults.standard
|
||||
var legacyData: [String: Any] = [:]
|
||||
|
||||
// 检查各种旧数据
|
||||
if let userId = userDefaults.string(forKey: LegacyStorageKeys.userId) {
|
||||
legacyData[LegacyStorageKeys.userId] = userId
|
||||
}
|
||||
|
||||
if let accessToken = userDefaults.string(forKey: LegacyStorageKeys.accessToken) {
|
||||
legacyData[LegacyStorageKeys.accessToken] = accessToken
|
||||
}
|
||||
|
||||
if let userInfoData = userDefaults.data(forKey: LegacyStorageKeys.userInfo) {
|
||||
legacyData[LegacyStorageKeys.userInfo] = userInfoData
|
||||
}
|
||||
|
||||
if let accountModelData = userDefaults.data(forKey: LegacyStorageKeys.accountModel) {
|
||||
legacyData[LegacyStorageKeys.accountModel] = accountModelData
|
||||
}
|
||||
|
||||
if let appLanguage = userDefaults.string(forKey: LegacyStorageKeys.appLanguage) {
|
||||
legacyData[LegacyStorageKeys.appLanguage] = appLanguage
|
||||
}
|
||||
|
||||
return legacyData
|
||||
}
|
||||
|
||||
/// 迁移数据到 Keychain
|
||||
private func migrateToKeychain(_ legacyData: [String: Any]) throws {
|
||||
let keychain = KeychainManager.shared
|
||||
|
||||
// 迁移 AccountModel(优先级最高,包含完整认证信息)
|
||||
if let accountModelData = legacyData[LegacyStorageKeys.accountModel] as? Data {
|
||||
do {
|
||||
let accountModel = try JSONDecoder().decode(AccountModel.self, from: accountModelData)
|
||||
try keychain.store(accountModel, forKey: "account_model")
|
||||
debugInfoSync("✅ AccountModel 迁移成功")
|
||||
} catch {
|
||||
debugErrorSync("❌ AccountModel 迁移失败: \(error)")
|
||||
// 如果 AccountModel 迁移失败,尝试从独立字段重建
|
||||
try migrateAccountModelFromIndependentFields(legacyData)
|
||||
}
|
||||
} else {
|
||||
// 如果没有 AccountModel,从独立字段构建
|
||||
try migrateAccountModelFromIndependentFields(legacyData)
|
||||
}
|
||||
|
||||
// 迁移 UserInfo
|
||||
if let userInfoData = legacyData[LegacyStorageKeys.userInfo] as? Data {
|
||||
do {
|
||||
let userInfo = try JSONDecoder().decode(UserInfo.self, from: userInfoData)
|
||||
try keychain.store(userInfo, forKey: "user_info")
|
||||
debugInfoSync("✅ UserInfo 迁移成功")
|
||||
} catch {
|
||||
debugErrorSync("❌ UserInfo 迁移失败: \(error)")
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// 迁移语言设置
|
||||
if let appLanguage = legacyData[LegacyStorageKeys.appLanguage] as? String {
|
||||
try keychain.storeString(appLanguage, forKey: "AppLanguage")
|
||||
debugInfoSync("✅ 语言设置迁移成功")
|
||||
}
|
||||
}
|
||||
|
||||
/// 从独立字段重建 AccountModel
|
||||
private func migrateAccountModelFromIndependentFields(_ legacyData: [String: Any]) throws {
|
||||
guard let userId = legacyData[LegacyStorageKeys.userId] as? String,
|
||||
let accessToken = legacyData[LegacyStorageKeys.accessToken] as? String else {
|
||||
debugInfoSync("ℹ️ 没有足够的独立字段来重建 AccountModel")
|
||||
return
|
||||
}
|
||||
|
||||
let accountModel = AccountModel(
|
||||
uid: userId,
|
||||
jti: nil,
|
||||
tokenType: "bearer",
|
||||
refreshToken: nil,
|
||||
netEaseToken: nil,
|
||||
accessToken: accessToken,
|
||||
expiresIn: nil,
|
||||
scope: nil,
|
||||
ticket: nil
|
||||
)
|
||||
|
||||
try KeychainManager.shared.store(accountModel, forKey: "account_model")
|
||||
debugInfoSync("✅ 从独立字段重建 AccountModel 成功")
|
||||
}
|
||||
|
||||
/// 验证迁移结果
|
||||
private func verifyMigration(_ legacyData: [String: Any]) throws {
|
||||
let keychain = KeychainManager.shared
|
||||
|
||||
// 验证 AccountModel
|
||||
if legacyData[LegacyStorageKeys.accountModel] != nil ||
|
||||
(legacyData[LegacyStorageKeys.userId] != nil && legacyData[LegacyStorageKeys.accessToken] != nil) {
|
||||
let accountModel: AccountModel? = try keychain.retrieve(AccountModel.self, forKey: "account_model")
|
||||
guard accountModel != nil else {
|
||||
throw MigrationError.verificationFailed("AccountModel 验证失败")
|
||||
}
|
||||
}
|
||||
|
||||
// 验证 UserInfo
|
||||
if legacyData[LegacyStorageKeys.userInfo] != nil {
|
||||
let userInfo: UserInfo? = try keychain.retrieve(UserInfo.self, forKey: "user_info")
|
||||
guard userInfo != nil else {
|
||||
throw MigrationError.verificationFailed("UserInfo 验证失败")
|
||||
}
|
||||
}
|
||||
|
||||
// 验证语言设置
|
||||
if legacyData[LegacyStorageKeys.appLanguage] != nil {
|
||||
let appLanguage = try keychain.retrieveString(forKey: "AppLanguage")
|
||||
guard appLanguage != nil else {
|
||||
throw MigrationError.verificationFailed("语言设置验证失败")
|
||||
}
|
||||
}
|
||||
|
||||
debugInfoSync("✅ 迁移数据验证成功")
|
||||
}
|
||||
|
||||
/// 清理旧数据
|
||||
private func cleanupLegacyData(_ keys: Dictionary<String, Any>.Keys) {
|
||||
let userDefaults = UserDefaults.standard
|
||||
|
||||
for key in keys {
|
||||
userDefaults.removeObject(forKey: key)
|
||||
debugInfoSync("🗑️ 清理旧数据: \(key)")
|
||||
}
|
||||
|
||||
userDefaults.synchronize()
|
||||
debugInfoSync("✅ 旧数据清理完成")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - 迁移错误
|
||||
|
||||
enum MigrationError: Error, LocalizedError {
|
||||
case verificationFailed(String)
|
||||
case dataCorrupted(String)
|
||||
case keychainError(Error)
|
||||
|
||||
var errorDescription: String? {
|
||||
switch self {
|
||||
case .verificationFailed(let message):
|
||||
return "验证失败: \(message)"
|
||||
case .dataCorrupted(let message):
|
||||
return "数据损坏: \(message)"
|
||||
case .keychainError(let error):
|
||||
return "Keychain 错误: \(error.localizedDescription)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - 应用启动时的迁移支持
|
||||
|
||||
extension DataMigrationManager {
|
||||
|
||||
/// 在应用启动时执行迁移
|
||||
/// 这个方法应该在 AppDelegate 或 App 的初始化阶段调用
|
||||
static func performStartupMigration() {
|
||||
let migrationResult = DataMigrationManager.shared.performMigration()
|
||||
|
||||
switch migrationResult {
|
||||
case .completed:
|
||||
debugInfoSync("🎉 应用启动时数据迁移完成")
|
||||
case .alreadyMigrated:
|
||||
break // 静默处理
|
||||
case .noDataToMigrate:
|
||||
break // 静默处理
|
||||
case .failed(let error):
|
||||
debugErrorSync("⚠️ 应用启动时数据迁移失败: \(error)")
|
||||
// 这里可以添加错误上报或降级策略
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - 调试支持
|
||||
|
||||
#if DEBUG
|
||||
extension DataMigrationManager {
|
||||
|
||||
/// 调试:打印旧数据信息
|
||||
func debugPrintLegacyData() {
|
||||
let legacyData = collectLegacyData()
|
||||
debugInfoSync("🔍 旧版本数据:")
|
||||
for (key, value) in legacyData {
|
||||
debugInfoSync(" - \(key): \(type(of: value))")
|
||||
}
|
||||
}
|
||||
|
||||
/// 调试:模拟创建旧数据(用于测试)
|
||||
func debugCreateLegacyData() {
|
||||
let userDefaults = UserDefaults.standard
|
||||
|
||||
userDefaults.set("test_user_123", forKey: LegacyStorageKeys.userId)
|
||||
userDefaults.set("test_access_token", forKey: LegacyStorageKeys.accessToken)
|
||||
userDefaults.set("zh-Hans", forKey: LegacyStorageKeys.appLanguage)
|
||||
userDefaults.synchronize()
|
||||
|
||||
debugInfoSync("🧪 已创建测试用的旧版本数据")
|
||||
}
|
||||
|
||||
/// 调试:清除所有迁移相关数据
|
||||
func debugClearAllData() {
|
||||
// 清除 Keychain 数据
|
||||
do {
|
||||
try KeychainManager.shared.clearAll()
|
||||
} catch {
|
||||
debugErrorSync("❌ 清除 Keychain 数据失败: \(error)")
|
||||
}
|
||||
|
||||
// 清除 UserDefaults 数据
|
||||
let userDefaults = UserDefaults.standard
|
||||
let allKeys = [
|
||||
LegacyStorageKeys.userId,
|
||||
LegacyStorageKeys.accessToken,
|
||||
LegacyStorageKeys.userInfo,
|
||||
LegacyStorageKeys.accountModel,
|
||||
LegacyStorageKeys.appLanguage,
|
||||
migrationCompleteKey
|
||||
]
|
||||
|
||||
for key in allKeys {
|
||||
userDefaults.removeObject(forKey: key)
|
||||
}
|
||||
userDefaults.synchronize()
|
||||
|
||||
debugInfoSync("🧪 已清除所有迁移相关数据")
|
||||
}
|
||||
}
|
||||
#endif
|
Reference in New Issue
Block a user