Search code examples
swiftamazon-dynamodbuicollectionviewcellaws-sdkaws-mobilehub

Retrieve an item from a table of Amazon DynamoDB, then pass it to be the numberOfItemsInSection (Swift 4)?


I'm trying to retrieve an item from a table (TheUserIds2018) of Amazon DynamoDB, and I would like to pass the item to be the numberOfItemsInSection of the UICollectionViewCell, here is the table TheUserIds2018 looks like:

enter image description here

Here are the codes:

override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(true)

    let dynamoDbObjectMapper = AWSDynamoDBObjectMapper.default()
        dynamoDbObjectMapper.load(TheUserIds2018.self, hashKey: "TheUserIds2018", rangeKey: nil, completionHandler: { (objectModel: AWSDynamoDBObjectModel?, error: Error?) -> Void in
            if let error = error {
                print("Amazon DynamoDB Read Error: \(error)")
                return
            }


                if let count = objectModel?.dictionaryValue["_articleCounters"] {
                    self.counters = count as! Int
                } 
self.collectionView?.reloadData()


        })

}

here is the numberOfItemsInSection:

 override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            return self.counters
}

But there is no collectionViewCell displaying in the screen when I build and run the app, it's blank:

enter image description here

I know the reason is that the collectionViewCell launches before than viewWillAppear. So I tried the following way, but sometimes it will crash (with the error: Thread 1: Fatal error: Index out of range):

Here are the working codes:

  override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(true)

    self.view.addSubview(activityView)
    activityView.hidesWhenStopped = true
    activityView.center = self.view.center
    activityView.startAnimating()

    let dynamoDbObjectMapper = AWSDynamoDBObjectMapper.default()
    dynamoDbObjectMapper.load(TheUserIds2018.self, hashKey: "TheUserIds2018", rangeKey: nil, completionHandler: { (objectModel: AWSDynamoDBObjectModel?, error: Error?) -> Void in
        if let error = error {
            print("Amazon DynamoDB Read Error: \(error)")
            return
        }

        DispatchQueue.main.async {
            if let count = objectModel?.dictionaryValue["_articleCounters"] {
                print("Count: \(count)")
                self.counters = count as! Int
                self.collectionView?.reloadData()
                if self.counters > 10 {
                    self.updateItems()
                } else {
                    self.getItems()
                }
              self.activityView.stopAnimating()
            }
        }
    })

}
func getItems() {
        let dynamoDbObjectMapper = AWSDynamoDBObjectMapper.default()
        var tasksList = Array<AWSTask<AnyObject>>()
        for i in 1...10 {
            tasksList.append(dynamoDbObjectMapper.load(RecommendArticles.self, hashKey: "userId" + String(11 - i), rangeKey: nil))
        }

        AWSTask<AnyObject>.init(forCompletionOfAllTasksWithResults: tasksList).continueWith { (task) -> Any? in
            if let cards = task.result as? [RecommendArticles] {
                    self.card = cards
                    print("cards counter: \(cards.count)")

            } else if let error = task.error {
                print(error.localizedDescription)
            }

            return nil

        }


    }

    func updateItems() {
        let dynamoDbObjectMapper = AWSDynamoDBObjectMapper.default()
        var tasksList = Array<AWSTask<AnyObject>>()
            for i in 1...self.counters {
                tasksList.append(dynamoDbObjectMapper.load(RecommendArticles.self, hashKey: "userId" + String(self.counters + 1 - i), rangeKey: nil))
            }
            AWSTask<AnyObject>.init(forCompletionOfAllTasksWithResults: tasksList).continueWith { (task) -> Any? in
                if let cards = task.result as? [RecommendArticles] {
                        self.card = cards
                        print("Real cards counter: \(cards.count)")

                } else if let error = task.error {
                    print(error.localizedDescription)
                }

                return nil

            }

    }

override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return self.counters

}



 override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: verticalCellId, for: indexPath) as! VerticalCellId

        if let link = self.card?[indexPath.item]._imageURL {
            let url = URL(string: link)
            cell.photoImageView.kf.setImage(with: url)

        }

    }

PS: There are two different tables in my AWS DynamoDB: TheUserIds2018 and RecommendArticles. Here is what the RecommendArticles looks like:

enter image description here

Any other way to retrieve the item from the table (TheUserIds2018), and then pass it to be the numberOfItemsInSection?

Update: after adding self.collectionView?.reloadDate() to main thread, it works well in most cases, but it sometimes crashes if try to scroll down immediately after the app launches, especially when in a bad internet connection. Same error: Thread 1: Fatal error: Index out of range. Any way to fix this?

Final Update: Finally, with the help of @Rohi, it works now. I have updated the codes to date.


Solution

  • Call reloadData() after you assign to self.counters in main thread it will work. Not before assigning self.counters.