For all solutions, note that there is no need to explicitly call reloadData in viewDidLoad: it will happen automatically.
Solution 1
Inspired by Samantha idea: invalidateLayout asynchronously in viewDidLoad.
override func viewDidLoad() {
    super.viewDidLoad()
    //[...]
    for _ in 0 ..< 1000 {
        array.append(randomKeyByBitLength(Int(arc4random_uniform(8)))!)
    }
    DispatchQueue.main.async {
        self.collectionView.collectionViewLayout.invalidateLayout()
    }
}
Solution 2
(imperfect, see DHennessy13 improvement on it)
Based on Peter Lapisu answer. invalidateLayout in viewWillLayoutSubviews.
override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()
    collectionView.collectionViewLayout.invalidateLayout()
}
As noted by DHennessy13, this current solution with viewWillLayoutSubviews is imperfect as it will invalidateLayout when rotating the screen.
You may follow DHennessy13 improvement regarding this solution.
Solution 3
Based on a combination of Tyler Sheaffer answer, Shawn Aukstak port to Swift and Samantha idea. Subclass your CollectionView to perform invalidateLayout on layoutSubviews.
class AutoLayoutCollectionView: UICollectionView {
    private var shouldInvalidateLayout = false
    override func layoutSubviews() {
        super.layoutSubviews()
        if shouldInvalidateLayout {
            collectionViewLayout.invalidateLayout()
            shouldInvalidateLayout = false
        }
    }
    override func reloadData() {
        shouldInvalidateLayout = true
        super.reloadData()
    }
}
This solution is elegant as it doesn't require to change your ViewController code. I've implemented it on branch AutoLayoutCollectionView of this sample project https://github.com/Coeur/StackOverflow51375566/tree/AutoLayoutCollectionView.
Solution 4
Rewrite UICollectionViewCell default constraints. See Larry answer.
Solution 5
Implement collectionView(_:layout:sizeForItemAt:) and return cell.contentView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize). See matt answer.