You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中Firestore评分读写:提交评分功能整合问题

实现星级评分提交并存储到Firestore

我需要实现用户提交星级评分并将其存储在对应文档下的集合中,已经编写了getRatingsSubcollection、addRatingTransaction函数,但不确定如何将这些函数整合到Rating视图结构中,以实现每次用户提交评分时自动运行。

现有代码

struct Rating: View {
    @State var userCurrentRating: CGFloat = 0.0
    var schools = ["North Avenue", "West Village", "Brittain"]
    @State private var location = "West Village"
    @State private var isUserRatingCurrent = true
    @State private var currentRating: CGFloat = 0.0
    var db: Firestore!
    var body: some View {
        NavigationView{
            ScrollView {
                VStack(spacing: 16) {
                    Divider()
                    Text("Choose a dining location:")
                        .bold()
                    Picker("Choose a dining location:", selection: $location) {
                        ForEach(schools, id: \.self) {
                            Text($0)
                        }
                    }
                    Text("Current Rating, \(currentRating)")
                        .bold()
                    if self.isUserRatingCurrent {
                        VStack {
                            RatingsViewCurrent(onDidRateCurrent: self.userDidRateCurrent, ratingCurrent: self.$userCurrentRating)
                                .frame(width: .infinity, height: 100, alignment: .center)
                        }
                    } else {
                        VStack {
                            RatingsViewCurrent(ratingCurrent: self.$currentRating)
                                .frame(width: .infinity, height: 100, alignment: .center)
                            if userCurrentRating == 0.0 {
                                Button("Rate") {
                                    self.isUserRatingCurrent = true
                                }
                            }
                        }
                    }
                }
            }.navigationTitle("Add a Rating")
        }
    }
    
    func userDidRateCurrent(_ ratingCurrent: Int) {            
        print("ratingcurrent \(ratingCurrent)")
    }
    
    struct diningLocation {
        let location: String
        let currentRating: Float
        let usualRating: Float
        let totalRatings: Float
        
        init(location: String, currentRating: Float, usualRating: Float, totalRatings: Float) {
            self.location = location
            self.currentRating = currentRating
            self.usualRating = usualRating
            self.totalRatings = totalRatings
        }
    }
    
    let WestVillage = diningLocation(location: "West Village", currentRating: 4.65, usualRating: 2.9, totalRatings: 45.0)
    
    struct Rating {
        let rating: Float
    }
    
    func getRatingsSubcollection() {
        db.collection("RatingInformation")
            .document("West Village")
            .collection("ratings")
            .getDocuments() { (querySnapshot, err) in
                // ...
            }
    }
    
    func addRatingTransaction(restaurantRef: DocumentReference, ratingCurrent: Float) {
        let ratingRef: DocumentReference = restaurantRef.collection("ratings").document()
        
        db.runTransaction({ (transaction, errorPointer) -> Any? in
            do {
                let restaurantDocument = try transaction.getDocument(restaurantRef).data()
                guard var restaurantData = restaurantDocument else { return nil }
                
                let totalRatings = restaurantData["totalRatings"] as! Int
                let newNumRatings = totalRatings + 1
                
                let usualRating = restaurantData["usualRating"] as! Float
                let oldRatingTotal = usualRating * Float(totalRatings)
                let newAvgRating = (oldRatingTotal + ratingCurrent) / Float(newNumRatings)
                
                restaurantData["totalRatings"] = newNumRatings
                restaurantData["usualRating"] = newAvgRating
                
                transaction.setData(restaurantData, forDocument: restaurantRef)
                transaction.setData(["rating": ratingCurrent], forDocument: ratingRef)
            } catch {
                // Error getting restaurant data
                // ...
            }
            
            return nil
        }) { (object, err) in
            // ...
        }
    }
}

整合方案

1. 初始化Firestore实例

在Rating视图的初始化方法中完成Firestore的初始化,确保db变量有值:

init() {
    db = Firestore.firestore()
    // 可选:配置Firestore持久化等设置
    // let settings = FirestoreSettings()
    // settings.isPersistenceEnabled = true
    // db.settings = settings
}

2. 关联评分提交到用户交互

修改userDidRateCurrent函数,在用户完成评分时触发存储逻辑,并更新本地视图状态:

func userDidRateCurrent(_ ratingCurrent: Int) {
    print("ratingcurrent \(ratingCurrent)")
    // 获取当前选中餐厅的Firestore文档引用
    let restaurantRef = db.collection("RatingInformation").document(location)
    // 调用事务存储评分并更新平均评分
    addRatingTransaction(restaurantRef: restaurantRef, ratingCurrent: Float(ratingCurrent))
    // 更新本地显示的评分值
    currentRating = CGFloat(ratingCurrent)
    userCurrentRating = CGFloat(ratingCurrent)
    // 切换到已评分状态,展示当前平均评分
    isUserRatingCurrent = false
}

3. 加载当前餐厅的实时评分

添加函数加载选中餐厅的当前平均评分,并在视图出现和切换餐厅时调用:

func loadCurrentRating() {
    let restaurantRef = db.collection("RatingInformation").document(location)
    restaurantRef.getDocument { snapshot, error in
        guard let data = snapshot?.data(), error == nil else {
            print("加载评分失败: \(error?.localizedDescription ?? "未知错误")")
            return
        }
        // 更新本地当前评分
        if let avgRating = data["usualRating"] as? Float {
            currentRating = CGFloat(avgRating)
        }
    }
}

给Picker添加选中变化监听,给ScrollView添加视图出现监听:

Picker("Choose a dining location:", selection: $location) {
    ForEach(schools, id: \.self) {
        Text($0)
    }
}
.onChange(of: location) { _ in
    loadCurrentRating()
}
ScrollView {
    // 原有视图内容
}
.navigationTitle("Add a Rating")
.onAppear {
    loadCurrentRating()
}

4. 完善错误处理

在addRatingTransaction中补充错误处理逻辑,便于调试:

func addRatingTransaction(restaurantRef: DocumentReference, ratingCurrent: Float) {
    let ratingRef: DocumentReference = restaurantRef.collection("ratings").document()
    
    db.runTransaction({ (transaction, errorPointer) -> Any? in
        do {
            let restaurantDocument = try transaction.getDocument(restaurantRef).data()
            guard var restaurantData = restaurantDocument else { 
                print("未找到餐厅文档")
                return nil 
            }
            
            guard let totalRatings = restaurantData["totalRatings"] as? Int,
                  let usualRating = restaurantData["usualRating"] as? Float else {
                print("餐厅数据格式错误")
                return nil
            }
            
            let newNumRatings = totalRatings + 1
            let oldRatingTotal = usualRating * Float(totalRatings)
            let newAvgRating = (oldRatingTotal + ratingCurrent) / Float(newNumRatings)
            
            restaurantData["totalRatings"] = newNumRatings
            restaurantData["usualRating"] = newAvgRating
            
            transaction.setData(restaurantData, forDocument: restaurantRef)
            transaction.setData(["rating": ratingCurrent], forDocument: ratingRef)
        } catch {
            print("获取餐厅数据出错: \(error.localizedDescription)")
        }
        
        return nil
    }) { (object, err) in
        if let err = err {
            print("事务执行失败: \(err.localizedDescription)")
        } else {
            print("评分提交成功")
        }
    }
}

5. 修复视图逻辑

调整"Rate"按钮的逻辑,支持重新评分:

if userCurrentRating == 0.0 {
    Button("Rate") {
        self.isUserRatingCurrent = true
    }
} else {
    Button("Rate Again") {
        self.isUserRatingCurrent = true
    }
}

内容的提问来源于stack exchange,提问作者FrancescaD

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 04:10:35