Home > Software engineering >  Sorting cells instead of table
Sorting cells instead of table

Time:12-29

My App checks which POI/ Feed/ Group the user is following. Based on that it loads comments with the content of the comment and the user name/picture. Those are loaded separately. My only issue is sorting by time.

If I use this code: self.table = self.table.sorted(by: { $0.userTime ?? 0 > $1.userTime ?? 0 }) the sorting of the comments is correct. But the matching of the profile names and pictures is totally wrong.

If I remove that code above the matching is correct but the sorting is totally wrong. How can I solve this? Instead of sorting the table I have to sort the cell itself?

func loadFollowedPoi() {
    myFeed.myArray1 = []
    let userID = Auth.auth().currentUser!.uid
    let database = Database.database().reference()
    database.child("user/\(userID)/abonniertePoi/").observeSingleEvent(of: .value, with: { snapshot in
        for child in snapshot.children.allObjects as! [DataSnapshot] {
            myFeed.myArray1.append(child.key)
        }
        self.postsLaden()
    })
}

func postsLaden() {
    dic = [:]
    
    let neueArray: [String] = []
    for groupId in myFeed.myArray1[0..<myFeed.myArray1.count] {
        
        let placeIdFromSearch = ViewController.placeidUebertragen
        ref = Database.database().reference().child("placeID/\(groupId)")
        ref.observe(DataEventType.childAdded, with: { snapshot in
            
            guard let dic = snapshot.value as? [String: Any] else { return }
            let newPost = importPosts(dictionary: dic, key: snapshot.key)
            guard let userUid = newPost.userID else { return }

            self.fetchUser(uid: userUid, completed: {
                self.table.insert(newPost, at: 0)
                self.table = self.table.sorted(by: { $0.userTime ?? 0 > $1.userTime ?? 0 })

                self.tableView.reloadData()
            })
        }
        )}
    
}


func fetchUser(uid: String, completed: @escaping () -> Void) {
    ref = Database.database().reference().child("user").child(uid).child("userInformation")
    ref.observe(.value) { (snapshot) in
        guard let dic = snapshot.value as? [String: Any] else { return }
        let newUser = UserModel(dictionary: dic)
        self.users.insert(newUser, at: 0)
        completed()
    }
}


func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
        cell.layoutMargins = UIEdgeInsets.zero
        cell.post = table[indexPath.row]
        
        cell.user = users[indexPath.row]
        
        return cell
}



class importPosts {
    var userID: String?
    var userGroup: String?
    var userComment: String?
    var userTime: Int?
    var userLikes: Int?
    var commentId: String?
    var placeID: String?
    var kommentarCount: Int?
    var id: String?
    
    var likeCount: Int?
    var likes: Dictionary<String, Any>?
    var isLiked: Bool?
    
    init(dictionary: [String: Any], key: String) {
        
        userID = dictionary["userID"] as? String
        userComment = dictionary["userComment"] as? String
        userGroup = dictionary["userGroup"] as? String
        userTime = dictionary["userTime"] as? Int
        userLikes = dictionary["userLikes"] as? Int
        commentId = dictionary["commentId"] as? String
        placeID = dictionary["placeID"] as? String
        kommentarCount = dictionary["kommentarCount"] as? Int
       
        id = key
        
        likeCount = dictionary["likeCount"] as? Int
        likes = dictionary["likes"] as? Dictionary<String, Any>
        
        ViewComments.commentIDNew = commentId!
        
        if let currentUserUid = Auth.auth().currentUser?.uid {
            if let likes = self.likes {
                isLiked = likes[currentUserUid] != nil
            }
        }
        
        
    }
    
}

CodePudding user response:

As suggested in the comments create a parent struct which contains one user an one post respectively

struct UserData {
    let user: UserModel
    let post: importPosts
}

Side note: Please name structs/classes always uppercase and why not simply User and Post?


Create the datasource array

var users = [UserData]()

Modify fetchUser to pass the new user in the completion handler

func fetchUser(uid: String, completed: @escaping (UserModel) -> Void) {
    ref = Database.database().reference().child("user").child(uid).child("userInformation")
    ref.observe(.value) { (snapshot) in
        guard let dic = snapshot.value as? [String: Any] else { return }
        let newUser = UserModel(dictionary: dic)
        completed(newUser)
    }
}

And modify also postsLaden to assign the post and the associated user to the model

func postsLaden() {
    //dic = [:] 
    //let neueArray: [String] = [] seems to be unused

    for groupId in myFeed.myArray1[0..<myFeed.myArray1.count] {
        
        let placeIdFromSearch = ViewController.placeidUebertragen
        ref = Database.database().reference().child("placeID/\(groupId)")
        ref.observe(DataEventType.childAdded, with: { snapshot in
            
            guard let dic = snapshot.value as? [String: Any] else { return }
            let newPost = importPosts(dictionary: dic, key: snapshot.key)
            guard let userUid = newPost.userID else { return }

            self.fetchUser(uid: userUid, completed: { user in
                self.users.insert(UserData(user: user, post: newPost), at: 0)
                self.users.sort($0.user.userTime ?? 0 > $1.user.userTime ?? 0)    
                self.tableView.reloadData()
            })
        }
        )}
    
}

Finally modify cellForRow

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
        cell.layoutMargins = UIEdgeInsets.zero
        let user = users[indexPath.row]
        cell.post = user.post
        cell.user = user.user
        
        return cell
}

Yet another side note: Sorting and reloading the table view multiple times inside the loop is unnecessarily expensive. You could add DispatchGroup to sort and reload the data once on completion. Regarding expensive: In the database isn't it possible that Post can hold a full reference to the user to avoid the second fetch? For example Core Data can.

  • Related