feat: 实现MomentListItem点赞功能及状态管理

- 在MomentListItem中新增点赞功能,用户点击按钮可触发点赞请求。
- 使用MVVM+Combine架构管理点赞状态,确保UI与状态同步。
- 添加加载状态和错误处理,提升用户体验和交互反馈。
- 更新相关视图以支持新的点赞逻辑,优化代码可读性和维护性。
This commit is contained in:
edwinQQQ
2025-08-07 11:50:30 +08:00
parent a340163490
commit 6b575dab27
6 changed files with 459 additions and 355 deletions

View File

@@ -0,0 +1,27 @@
✅ [API Response] [11:19:32.208] ===================
⏱️ Duration: 0.258s
📊 Status Code: 200
🔗 URL: https://api.epartylive.com/dynamic/like?uid=7&likedUid=563&status=1&worldId=-1&dynamicId=8
📏 Data Size: 0 KB
📋 Response Headers:
Alt-Svc: h3=":443"; ma=2592000, h3-29=":443"; ma=2592000, h3-27=":443"; ma=2592000, h3-Q050=":443"; ma=2592000, h3-Q046=":443"; ma=2592000, h3-Q043=":443"; ma=2592000, h3-Q039=":443"; ma=2592000, quic=":443"; ma=2592000; v="39,43,46"
Content-Length: 58
Content-Type: application/json
Date: Thu, 07 Aug 2025 03:19:34 GMT
Server: TencentEdgeOne
Vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers
eo-cache-status: MISS
eo-log-uuid: 6089645366037004798
📦 Response Data:
{
"message" : "success",
"timestamp" : 1754536774238,
"code" : 200
}
=====================================
🎯 [Decoded Response] [11:19:32.210] Type: LikeDynamicResponse
=====================================
[error] ❌ MomentListItem: 点赞操作失败
[error] 动态ID: 8
[error] 错误: success

51
Debug/debug info.txt Normal file
View File

@@ -0,0 +1,51 @@
warning: (arm64) /Users/edwinqqq/Library/Developer/Xcode/DerivedData/yana-fuvanhpzisxarwhiosnkkltamhjw/Build/Products/Debug-iphoneos/yana.app/yana empty dSYM file detected, dSYM was created with an executable with no debug info.
[info] 🔐 Keychain 读取成功: AppLanguage
[info] 🔍 Loading items updated: 0 items
[info] 🔐 Keychain 读取成功: account_model
[info] 🔍 认证检查:认证有效 - uid: 563, ticket: eyJhbGciOi...
[info] 🎉 自动登录成功,开始获取用户信息
[info] 🔍 认证检查:认证有效 - uid: 563, ticket: eyJhbGciOi...
[info] 🔐 Keychain 读取成功: user_info
[info] 📱 APP启动使用现有用户信息缓存
[info] ✅ 用户信息获取成功,进入主页
[info] 🏗️ MainFeature 初始化
[info] accountModel.uid: nil
[info] 转换后的uid: 0
[info] 🔍 尝试从Keychain获取AccountModel
[info] ✅ 从Keychain获取到AccountModel: 563
[info] meState.uid: 0
[info] meState.displayUID: -1
[info] meState.effectiveUID: 0
[info] 🔍 BottomTabView get: MainFeature.Tab.feed → BottomTabView.Tab.feed
[info] 📱 MainContentView selectedTab: feed
[info] 与store.selectedTab一致: true
[info] 📱 FeedListContentView 状态:
[info] isLoading: false
[info] error: nil
[info] moments.count: 0
[info] hasMore: true
[info] 🔍 BottomTabView get: MainFeature.Tab.feed → BottomTabView.Tab.feed
[info] 🔍 Loading items updated: 0 items
[info] 🚀 MainView onAppear
[info] 当前selectedTab: feed
[info] 📦 MainFeature: AccountModel已加载
[info] uid: 563
[info] 🔄 更新MeFeature状态uid: 563
[info] ✅ FeedListFeature: 认证信息已准备好,开始获取动态
[info] 🏗️ MainFeature 初始化
[info] accountModel.uid: nil
[info] 转换后的uid: 0
[info] 🔍 尝试从Keychain获取AccountModel
[info] meState.uid: 0
[info] meState.displayUID: -1
[info] meState.effectiveUID: 0
[info] ✅ 从Keychain获取到AccountModel: 563
[info] 🔍 BottomTabView get: MainFeature.Tab.feed → BottomTabView.Tab.feed
[info] 📱 MainContentView selectedTab: feed
[info] 与store.selectedTab一致: true
[info] 📱 FeedListContentView 状态:
[info] isLoading: false
[info] error: nil
[info] moments.count: 0
[info] hasMore: true
[info] 🔍 BottomTabView get: MainFeature.Tab.feed → BottomTabView.Tab.feed

View File

@@ -0,0 +1,225 @@
# MomentListItem 点赞功能实现 (MVVM+Combine)
## 需求分析
1. 用户可以点击 like 按钮
2. 点击 like 按钮时,触发 LikeDynamicRequest 请求
3. 当 moment.isLike 为 true 时,请求的 status 参数传 0取消点赞
4. 当 moment.isLike 为 false 时,请求的 status 参数传 1点赞
5. 请求成功后,更新 MomentListItem 的 like 状态
## 架构选择
**使用 MVVM+Combine 架构**,参考 MomentListHomeViewModel 的实现模式:
- 不使用 TCA 框架
- 使用 @State 管理本地状态
- 使用 LiveAPIService 直接发起 API 请求
- 使用 Task 和 async/await 处理异步操作
## 实施计划
### 文件结构
- ✅ 修改:`yana/MVVM/View/MomentListItem.swift`
### 核心组件设计
1. **状态管理**
- `@State private var isLikeLoading = false` - 点赞加载状态
- `@State private var localIsLike: Bool` - 本地点赞状态
- `@State private var localLikeCount: Int` - 本地点赞数量
2. **API 请求**
- 使用 `LiveAPIService()` 直接创建服务实例
- 使用 `UserInfoManager.getCurrentUserId()` 获取当前用户ID
- 使用 `LikeDynamicRequest` 创建请求
3. **点赞处理逻辑**
- `handleLikeTap()` - 处理点赞按钮点击
- `performLikeRequest()` - 执行点赞 API 请求
### 实施步骤
1. ✅ 移除 TCA 相关导入和依赖
2. ✅ 添加 @State 状态变量
3. ✅ 实现点赞按钮的点击处理
4. ✅ 实现 API 请求逻辑(参考 MomentListHomeViewModel
5. ✅ 更新 UI 显示状态
6. ✅ 添加错误处理和加载状态
### 技术要点
- 使用 `LiveAPIService()` 直接创建服务实例
- 使用 `UserInfoManager.getCurrentUserId()` 获取当前用户ID
- 使用 `APILoadingManager` 显示错误信息
- 使用 `debugInfoSync``debugErrorSync` 记录日志
- 使用 `MainActor.run` 确保 UI 更新在主线程
## 实现细节
### 状态初始化
```swift
init(moment: MomentsInfo, onImageTap: @escaping (([String], Int)) -> Void = { (arg) in let (_, _) = arg; }) {
self.moment = moment
self.onImageTap = onImageTap
// 初始化本地状态
self._localIsLike = State(initialValue: moment.isLike)
self._localLikeCount = State(initialValue: moment.likeCount)
}
```
### 点赞按钮 UI
```swift
Button(action: {
if !isLikeLoading {
handleLikeTap()
}
}) {
HStack(spacing: 4) {
if isLikeLoading {
ProgressView()
.progressViewStyle(CircularProgressViewStyle(tint: localIsLike ? .red : .white.opacity(0.8)))
.scaleEffect(0.8)
} else {
Image(systemName: localIsLike ? "heart.fill" : "heart")
.font(.system(size: 16))
}
Text("\(localLikeCount)")
.font(.system(size: 14))
}
.foregroundColor(localIsLike ? .red : .white.opacity(0.8))
}
.disabled(isLikeLoading)
```
### API 请求逻辑
```swift
private func performLikeRequest() async {
// 设置加载状态
await MainActor.run {
isLikeLoading = true
}
do {
// 获取当前用户ID
guard let currentUserId = await UserInfoManager.getCurrentUserId(),
let currentUserIdInt = Int(currentUserId) else {
await MainActor.run {
isLikeLoading = false
}
setAPILoadingErrorSync(UUID(), errorMessage: "无法获取用户信息,请重新登录")
return
}
// 确定请求参数
let status = localIsLike ? 0 : 1 // 0: 取消点赞, 1: 点赞
// 创建 API 服务实例
let apiService = LiveAPIService()
// 创建请求
let request = LikeDynamicRequest(
dynamicId: moment.dynamicId,
uid: moment.uid,
status: status,
likedUid: currentUserIdInt,
worldId: moment.worldId
)
debugInfoSync("📡 MomentListItem: 发送点赞请求")
debugInfoSync(" 动态ID: \(moment.dynamicId)")
debugInfoSync(" 当前状态: \(localIsLike)")
debugInfoSync(" 请求状态: \(status)")
// 发起请求
let response: LikeDynamicResponse = try await apiService.request(request)
await MainActor.run {
isLikeLoading = false
// 处理响应
if let data = response.data, let success = data.success, success {
// 更新本地状态
localIsLike = !localIsLike
localLikeCount = data.likeCount ?? localLikeCount
debugInfoSync("✅ MomentListItem: 点赞操作成功")
debugInfoSync(" 动态ID: \(moment.dynamicId)")
debugInfoSync(" 新状态: \(localIsLike)")
debugInfoSync(" 新数量: \(localLikeCount)")
} else {
// 显示错误信息
let errorMessage = response.message.isEmpty ? "点赞失败,请重试" : response.message
setAPILoadingErrorSync(UUID(), errorMessage: errorMessage)
debugErrorSync("❌ MomentListItem: 点赞操作失败")
debugErrorSync(" 动态ID: \(moment.dynamicId)")
debugErrorSync(" 错误: \(errorMessage)")
}
}
} catch {
await MainActor.run {
isLikeLoading = false
}
setAPILoadingErrorSync(UUID(), errorMessage: error.localizedDescription)
debugErrorSync("❌ MomentListItem: 点赞请求异常")
debugErrorSync(" 动态ID: \(moment.dynamicId)")
debugErrorSync(" 错误: \(error.localizedDescription)")
}
}
```
## 架构对比
### 与 TCA 架构的区别
| 方面 | TCA 架构 | MVVM+Combine 架构 |
|------|----------|-------------------|
| 依赖注入 | @Dependency(\.apiService) | LiveAPIService() |
| 状态管理 | @ObservableState | @State |
| 异步处理 | Effect.task | Task + async/await |
| 错误处理 | 通过 Effect 处理 | 直接 try-catch |
| 复杂度 | 较高 | 较低 |
### 与 MomentListHomeViewModel 的一致性
- ✅ 使用相同的 API 服务创建方式
- ✅ 使用相同的错误处理模式
- ✅ 使用相同的日志记录方式
- ✅ 使用相同的用户验证逻辑
## 功能特性
### 交互体验
- **即时反馈**:点击后立即显示加载状态
- **状态切换**:成功后在点赞/取消点赞状态间切换
- **数量更新**:实时更新点赞数量显示
- **错误处理**:网络错误或服务器错误时显示友好提示
### 状态管理
- **本地状态**:使用 `@State` 管理本地点赞状态,避免影响其他组件
- **加载状态**:防止重复点击,提供视觉反馈
- **错误恢复**:请求失败时保持原有状态
### 安全性
- **用户验证**:确保用户已登录才能点赞
- **参数验证**:正确传递点赞状态参数
- **错误边界**:完善的错误处理机制
## 测试要点
1. 点赞状态切换正确true → false, false → true
2. 点赞数量实时更新
3. 加载状态显示正常
4. 网络错误处理正确
5. 用户未登录时的错误提示
6. 重复点击防护
7. 与其他组件的状态同步
## 完成状态
- [x] 移除 TCA 相关代码
- [x] 实现 MVVM+Combine 架构
- [x] 实现状态管理
- [x] 实现点赞按钮 UI
- [x] 实现 API 请求逻辑
- [x] 实现错误处理
- [x] 实现加载状态
- [x] 添加日志记录
- [x] 代码审查和优化
## 注意事项
1. 本实现使用本地状态管理,不会影响其他使用相同动态数据的组件
2. 如果需要全局状态同步,建议在父组件中实现状态管理
3. 点赞操作是幂等的,重复请求不会产生副作用
4. 错误处理使用全局的 APILoadingManager确保用户体验一致
5. 架构选择符合项目要求,不使用 TCA 框架

View File

@@ -40,7 +40,8 @@ struct MomentListHomePage: View {
.padding(.top, 16) .padding(.top, 16)
// //
Text(LocalizedString("feedList.slogan", comment: "The disease is like a cruel ruler,\nand time is our most precious treasure.\nEvery moment we live is a victory\nagainst the inevitable.")) Text(LocalizedString("feedList.slogan",
comment: ""))
.font(.system(size: 16)) .font(.system(size: 16))
.multilineTextAlignment(.leading) .multilineTextAlignment(.leading)
.foregroundColor(.white.opacity(0.9)) .foregroundColor(.white.opacity(0.9))
@@ -64,7 +65,8 @@ struct MomentListHomePage: View {
debugInfoSync(" 图片数量: \(images.count)") debugInfoSync(" 图片数量: \(images.count)")
} }
) )
.padding(.horizontal, 16) .padding(.leading, 16)
.padding(.trailing, 32)
.onAppear { .onAppear {
// //
if index == viewModel.moments.count - 3 { if index == viewModel.moments.count - 3 {
@@ -94,7 +96,7 @@ struct MomentListHomePage: View {
.padding(.vertical, 20) .padding(.vertical, 20)
} }
} }
.padding(.bottom, 100) // .padding(.bottom, 160) //
} }
.refreshable { .refreshable {
// //

View File

@@ -5,12 +5,20 @@ struct MomentListItem: View {
let moment: MomentsInfo let moment: MomentsInfo
let onImageTap: (([String], Int)) -> Void // let onImageTap: (([String], Int)) -> Void //
//
@State private var isLikeLoading = false
@State private var localIsLike: Bool
@State private var localLikeCount: Int
init( init(
moment: MomentsInfo, moment: MomentsInfo,
onImageTap: @escaping (([String], Int)) -> Void = { (arg) in let (_, _) = arg; } onImageTap: @escaping (([String], Int)) -> Void = { (arg) in let (_, _) = arg; }
) { ) {
self.moment = moment self.moment = moment
self.onImageTap = onImageTap self.onImageTap = onImageTap
//
self._localIsLike = State(initialValue: moment.isLike)
self._localLikeCount = State(initialValue: moment.likeCount)
} }
var body: some View { var body: some View {
@@ -84,13 +92,26 @@ struct MomentListItem: View {
// //
HStack(spacing: 20) { HStack(spacing: 20) {
// Like // Like
HStack(spacing: 4) { Button(action: {
Image(systemName: moment.isLike ? "heart.fill" : "heart") if !isLikeLoading {
.font(.system(size: 16)) handleLikeTap()
Text("\(moment.likeCount)") }
.font(.system(size: 14)) }) {
HStack(spacing: 4) {
if isLikeLoading {
ProgressView()
.progressViewStyle(CircularProgressViewStyle(tint: localIsLike ? .red : .white.opacity(0.8)))
.scaleEffect(0.8)
} else {
Image(systemName: localIsLike ? "heart.fill" : "heart")
.font(.system(size: 16))
}
Text("\(localLikeCount)")
.font(.system(size: 14))
}
.foregroundColor(localIsLike ? .red : .white.opacity(0.8))
} }
.foregroundColor(moment.isLike ? .red : .white.opacity(0.8)) .disabled(isLikeLoading)
.padding(.leading, 40 + 8) // + .padding(.leading, 40 + 8) // +
Spacer() Spacer()
} }
@@ -100,6 +121,83 @@ struct MomentListItem: View {
} }
} }
// MARK: -
private func handleLikeTap() {
Task {
await performLikeRequest()
}
}
private func performLikeRequest() async {
//
await MainActor.run {
isLikeLoading = true
}
do {
// ID
guard let currentUserId = await UserInfoManager.getCurrentUserId(),
let currentUserIdInt = Int(currentUserId) else {
await MainActor.run {
isLikeLoading = false
}
setAPILoadingErrorSync(UUID(), errorMessage: "无法获取用户信息,请重新登录")
return
}
//
let status = localIsLike ? 0 : 1 // 0: , 1:
// API
let apiService = LiveAPIService()
//
let request = LikeDynamicRequest(
dynamicId: moment.dynamicId,
uid: currentUserIdInt,
status: status,
likedUid: moment.uid,
worldId: moment.worldId
)
debugInfoSync("📡 MomentListItem: 发送点赞请求")
debugInfoSync(" 动态ID: \(moment.dynamicId)")
debugInfoSync(" 当前状态: \(localIsLike)")
debugInfoSync(" 请求状态: \(status)")
//
let response: LikeDynamicResponse = try await apiService.request(request)
await MainActor.run {
isLikeLoading = false
// , code
if response.code == 200 {
localIsLike = !localIsLike
localLikeCount = localIsLike ? localLikeCount+1 : localLikeCount-1
debugInfoSync("✅ MomentListItem: 点赞操作成功")
debugInfoSync(" 动态ID: \(moment.dynamicId)")
debugInfoSync(" 新状态: \(localIsLike)")
debugInfoSync(" 新数量: \(localLikeCount)")
} else {
let errorMessage = response.message.isEmpty ? "点赞失败,请重试" : response.message
setAPILoadingErrorSync(UUID(), errorMessage: errorMessage)
debugErrorSync("❌ MomentListItem: 点赞操作失败")
debugErrorSync(" 动态ID: \(moment.dynamicId)")
debugErrorSync(" 错误: \(errorMessage)")
}
}
} catch {
await MainActor.run {
isLikeLoading = false
}
setAPILoadingErrorSync(UUID(), errorMessage: error.localizedDescription)
debugErrorSync("❌ MomentListItem: 点赞请求异常")
debugErrorSync(" 动态ID: \(moment.dynamicId)")
debugErrorSync(" 错误: \(error.localizedDescription)")
}
}
// MARK: - // MARK: -
private func formatDisplayTime(_ timestamp: Int) -> String { private func formatDisplayTime(_ timestamp: Int) -> String {
let date = Date(timeIntervalSince1970: TimeInterval(timestamp) / 1000.0) let date = Date(timeIntervalSince1970: TimeInterval(timestamp) / 1000.0)
@@ -251,50 +349,50 @@ struct MomentSquareImageView: View {
} }
} }
#Preview { //#Preview {
// // //
let testMoment = MomentsInfo( // let testMoment = MomentsInfo(
dynamicId: 1, // dynamicId: 1,
uid: 123456, // uid: 123456,
nick: "测试用", // nick: "",
avatar: "", // avatar: "",
type: 0, // type: 0,
content: "这是一条测试动态内容,用来测试 MomentListItem 的显示效果", // content: " MomentListItem ",
likeCount: 42, // likeCount: 42,
isLike: false, // isLike: false,
commentCount: 5, // commentCount: 5,
publishTime: Int(Date().timeIntervalSince1970 * 1000), // publishTime: Int(Date().timeIntervalSince1970 * 1000),
worldId: 1, // worldId: 1,
status: 1, // status: 1,
playCount: nil, // playCount: nil,
dynamicResList: [ // dynamicResList: [
MomentsPicture(id: 1, resUrl: "https://picsum.photos/300/300", format: "jpg", width: 300, height: 300, resDuration: nil), // MomentsPicture(id: 1, resUrl: "https://picsum.photos/300/300", format: "jpg", width: 300, height: 300, resDuration: nil),
MomentsPicture(id: 2, resUrl: "https://picsum.photos/301/301", format: "jpg", width: 301, height: 301, resDuration: nil) // MomentsPicture(id: 2, resUrl: "https://picsum.photos/301/301", format: "jpg", width: 301, height: 301, resDuration: nil)
], // ],
gender: nil, // gender: nil,
squareTop: nil, // squareTop: nil,
topicTop: nil, // topicTop: nil,
newUser: nil, // newUser: nil,
defUser: nil, // defUser: nil,
scene: nil, // scene: nil,
userVipInfoVO: nil, // userVipInfoVO: nil,
headwearPic: nil, // headwearPic: nil,
headwearEffect: nil, // headwearEffect: nil,
headwearType: nil, // headwearType: nil,
headwearName: nil, // headwearName: nil,
headwearId: nil, // headwearId: nil,
experLevelPic: nil, // experLevelPic: nil,
charmLevelPic: nil, // charmLevelPic: nil,
isCustomWord: nil, // isCustomWord: nil,
labelList: nil // labelList: nil
) // )
//
MomentListItem( // MomentListItem(
moment: testMoment, // moment: testMoment,
onImageTap: { images, index in // onImageTap: { images, index in
print("图片被点击: 索引 \(index), 图片数量 \(images.count)") // print(": \(index), \(images.count)")
} // }
) // )
.padding() // .padding()
.background(Color.black) // .background(Color.black)
} //}

View File

@@ -1,299 +0,0 @@
# Yana 项目问题排查与解决流程文档
## 目录
1. [问题概述](#问题概述)
2. [解决流程](#解决流程)
3. [技术细节](#技术细节)
4. [最终解决方案](#最终解决方案)
5. [预防措施](#预防措施)
6. [常见问题FAQ](#常见问题faq)
---
## 问题概述
### 初始错误
**错误信息**: `"Could not compute dependency graph: unable to load transferred PIF: The workspace contains multiple references with the same GUID"`
**问题表现**:
- 项目无法启动
- Xcode 无法计算依赖图
- 出现 GUID 冲突错误
### 根本原因分析
1. **混合包管理系统**: 项目同时使用了 Swift Package Manager (SPM) 和 CocoaPods
2. **缓存冲突**: Xcode DerivedData 与 SPM 状态不同步
3. **TCA 结构问题**: 代码中 HomeFeature 缺少必要的状态和 Action 定义
---
## 解决流程
### 第一阶段GUID 冲突解决
#### 步骤 1: 清理缓存
```bash
# 清理 Xcode DerivedData
rm -rf ~/Library/Developer/Xcode/DerivedData/*
# 重置 Swift Package Manager
swift package reset
swift package resolve
```
#### 步骤 2: 重新安装 CocoaPods
```bash
pod install --clean-install
```
#### 步骤 3: 验证项目解析
```bash
xcodebuild -workspace yana.xcworkspace -list
```
### 第二阶段TCA 结构修复
#### 问题识别
- `HomeFeature.State` 缺少 `isSettingPresented``settingState` 属性
- `HomeFeature.Action` 缺少 `settingDismissed``setting` actions
- `HomeView.swift` 中的 `store.scope()` 调用语法错误
#### 修复步骤
1. 修复 HomeFeature.swift
```swift
@ObservableState
struct State: Equatable {
var isInitialized = false
var userInfo: UserInfo?
var accountModel: AccountModel?
var error: String?
// 添加设置页面相关状态
var isSettingPresented = false
var settingState = SettingFeature.State()
}
enum Action: Equatable {
case onAppear
case loadUserInfo
case userInfoLoaded(UserInfo?)
case loadAccountModel
case accountModelLoaded(AccountModel?)
case logoutTapped
case logout
// 添加设置页面相关actions
case settingDismissed
case setting(SettingFeature.Action)
}
```
2.添加子 Reducer
```swift
var body: some ReducerOf<Self> {
Scope(state: \.settingState, action: \.setting) {
SettingFeature()
}
Reduce { state, action in
// ... existing cases ...
case .settingDismissed:
state.isSettingPresented = false
return .none
case .setting:
// 由子reducer处理
return .none
}
}
```
3.修复 HomeView.swift
```swift
.sheet(isPresented: Binding(
get: { store.isSettingPresented },
set: { _ in store.send(.settingDismissed) }
)) {
SettingView(store: store.scope(state: \.settingState, action: \.setting))
}
```
---
## 技术细节
### 依赖管理配置
**Swift Package Manager (Package.swift)**:
- ComposableArchitecture: 1.20.2+
- 其他依赖根据需要添加
**CocoaPods (Podfile)**:
- Alamofire (网络请求)
- SDWebImage (图像加载)
- CocoaLumberjack (日志)
- 其他 UI 相关库
### TCA 架构模式
```
Feature
├── State (数据状态)
├── Action (用户操作)
├── Reducer (状态转换逻辑)
└── Dependencies (外部依赖)
```
### 文件结构
```
yana/
├── Features/ # TCA Feature 定义
├── Views/ # SwiftUI 视图
├── APIs/ # 网络 API 层
├── Utils/ # 工具类
└── Managers/ # 管理器类
```
---
## 最终解决方案
### 命令执行顺序
```bash
# 1. 清理环境
rm -rf ~/Library/Developer/Xcode/DerivedData/*
swift package reset
# 2. 重新解析依赖
swift package resolve
pod install --clean-install
# 3. 验证项目
xcodebuild -workspace yana.xcworkspace -scheme yana -configuration Debug build
```
### 关键代码修改
1. **HomeFeature.swift**: 添加设置相关状态管理
2. **HomeView.swift**: 修复 TCA store 绑定语法
3. **SettingFeature.swift**: 确保 Action 完整性
### 构建结果
**编译成功**: Exit code 0
⚠️ **警告信息**: 仅 Swift 6 兼容性警告,不影响运行
---
## 预防措施
### 开发规范
1. **统一包管理**: 优先使用一种包管理工具
2. **定期清理**: 定期清理 DerivedData 避免缓存问题
3. **代码审查**: 确保 TCA Feature 结构完整
4. **版本控制**: 及时提交关键配置文件
### 监控指标
- [ ] 项目编译时间 < 30s
- [ ] 无编译错误
- [ ] 依赖解析正常
- [ ] TCA 结构完整
### 工具使用
```bash
# 项目健康检查脚本
check_project() {
echo "🔍 检查项目状态..."
xcodebuild -workspace yana.xcworkspace -list > /dev/null 2>&1
if [ $? -eq 0 ]; then
echo "✅ 项目解析正常"
else
echo "❌ 项目解析失败,需要执行清理流程"
return 1
fi
}
```
---
## 常见问题FAQ
### Q1: 再次出现 GUID 冲突怎么办?
**A**: 执行完整清理流程
```bash
rm -rf ~/Library/Developer/Xcode/DerivedData/*
swift package reset && swift package resolve
pod install --clean-install
```
### Q2: TCA Reducer 编译错误如何处理?
**A**: 检查以下项目:
- State 属性完整性
- Action 枚举完整性
- Reducer body 中的 case 处理
- 子 Reducer 的 Scope 配置
### Q3: 如何避免混合包管理器问题?
**A**:
- 尽量使用单一包管理工具
- 如需混合使用,确保依赖版本兼容
- 定期更新依赖并测试
### Q4: Swift 6 兼容性警告如何处理?
**A**:
- 短期:可以忽略,不影响功能
- 长期:逐步迁移到 Swift 6 Sendable 模式
### Q5: 项目构建缓慢怎么办?
**A**:
- 使用 `xcodebuild -quiet` 减少输出
- 开启 Xcode Build System 并行构建
- 定期清理 DerivedData
---
## 总结
本次问题解决涉及以下关键技术点
1. **Xcode 项目配置管理**
2. **Swift Package Manager 与 CocoaPods 共存**
3. **TCA (The Composable Architecture) 最佳实践**
4. **iOS 开发环境故障排除**
通过系统性的排查和修复项目现已恢复正常运行状态建议团队建立定期维护机制避免类似问题再次发生
---
**文档更新时间**: 2025-07-10
**适用版本**: iOS 17+, Swift 6, TCA 1.20.2+
**维护者**: AI Assistant & 开发团队