首页 > 解决方案 > Swift iOS - 如何在 Firebase TransactionBlock 上放置计时器以防止它在特定时间段内增加

问题描述

每次不同的用户发布一些东西(比如说一种颜色)时,我都会得到他们发布的颜色、postID、他们的 userId、以秒为单位的日期以及该帖子被查看的次数。

不同的用户可以查看 tableView 并查看每个用户发布的每种颜色的不同单元格。

每次正在查看的用户点击didSelectRow查看颜色的详细视图时,我都会运行一个Firebase TransactionBlock增加views计数属性以显示特定颜色/单元格被点击的次数。

例如,如果用户滚动一个 tableView 并看到一个 blueCell,则会在其上显示一个标签,上面写着views: 10(意味着它被查看了 10 次)。如果该用户再次按下该 blueCell ,则视图计数将显示views: 11

问题是如果该用户反复按下该单元格,那么他们可以在几秒钟内增加该标签上的值countviews

我如何跟踪用户点击的每个对象/单元格并在其上放置一个计时器,以便他们无法在views count一个小时左右的时间内更新该特定对象的值?我有以秒为单位的日期和 postId,它们对每个对象都是唯一的。

基本上,如果用户在下午 12 点按下 blueCell,则与该特定单元格关联的对象的视图计数将上升到 11,但如果他们在下午 12 点至下午 1 点之间的任何时间再次按下它,它就不会上升。下午 1 点之后,如果他们再次按下它,该对象的观看次数将上升到 12?

我可以用来识别每个颜色对象的模型对象和属性:

class ColorClass{
    var color: String?
    var postID: String?
    var userId: String?
    var date: NSNumber?
    var views: NSNumber? // keeps track of how many the post was viewed
}

TableView 的 didSelectRow:

// the current user who is pressing the cell
let currentUserID = Auth.auth().currentUser?.uid
var colors = [ColorClass]() // 500 model objects

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return colors.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "ColorsCell", for: indexPath) as! ColorsCell

    cell.viewsLabel.text = colors[indexPath.row].views // I separately convert this from a NSNumber to a String
    cell.colorLabel.text = colors[indexPath.row].color

    return cell
}

// pressing the cell will increase the count on the object's views property
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

    guard let indexPath = tableView.indexPathForSelectedRow else { return }

    // the userId on the object of the cell that was pressed
    guard let userID = colors[indexPath.row].userId else { return }
    guard let postID = colors[indexPath.row].postId else { return }

    // make sure the current user can't update the views on their own post
    if currentUserID != userID{

        let viewsRef = databaseRef?.child(userID).child(postID).child("views")

        viewsRef?.runTransactionBlock({
            (currentData: MutableData) -> TransactionResult in

            let newValue: Int

            guard let existingValue = (currentData.value as? NSNumber)?.intValue else {
                return TransactionResult.abort()
            }

            newValue = existingValue + 1

            currentData.value = NSNumber(value: newValue)

            return TransactionResult.success(withValue: currentData)

        }, andCompletionBlock: {
            (error, completion, snap) in

            print(snap as Any)

            if !completion{
                print("The value wasn't able to update")
                print(error?.localizedDescription as Any)
            }else{
                print("The value updated")
            }
        })
    }
}

只是一个想法。

我考虑过创建另一个具有 currentUserID、postID 和 tappedTime 属性的对象。然后我会创建一个单例。每次按下单元格时,我都会将数据传递到对象中,然后将对象发送到单例中的数组。在那里我有一个 currentTime 属性。首先,我会检查 postID 是否在数组中,如果是,我会将 tappedTime 与 currentTime + 1 小时进行比较,以确定是否应该增加观看次数。我有一个调度异步计时器,1 小时后它会自动从阵列中清除。我不确定它有多实用。

标签: iosswiftfirebasefirebase-realtime-databasetransactions

解决方案


您可以创建一个typealias由您正在填充单元格的任何对象组成,并Date在视图控制器的顶部创建一个,如下所示:

  typealias ColorLastSelected = (colorClass: ColorClass, timeSelected: Date)

然后,创建一个数组来存储ColorLastSelected对象。

  var selectedColors: [ColorLastSelected] = []

从那里,在 中didSelectRow,您可以执行一个保护语句来检查一个对象是否包含在selectedColors数组中。如果没有,那么做任何你必须做的事情,最后,初始化一个ColorLastSelected对象并将其附加到selectedColors数组中。

在保持selectedColors最新方面,您可以在重复计时器上运行更新方法以删除ColorLastSelected超过 1 小时的 s。或者,您可以selectedColors在保护语句之前过滤数组以删除超过一个小时的内容。如果您要在视图控制器之间跳来跳去,您可能需要创建一个“保持活动状态”的单例,或者您可以将selectedColors数组保存在某个地方


推荐阅读