InfiniteIterator in Swift
Earlier this week a designer came to me with a new design for the app I’m currently working on. He had designed a more interesting article-feed page layout.
The layout contained a header element, followed by 5 cells in various positions. The designer then requested that the layout repeat itself (minus the header) as long as there is more content to display.
Iterator
So at this point I knew I wanted to use a UICollectionViewLayout
– because this gives us a nice abstraction from the underlying view and data.
I also decided to implement a separate type that would be responsible for calculating the frame of each cell in the layout.
public struct Layout {
public func frame(for element: Element, in bounds: CGRect) -> CGRect
}
What is an Iterator?
I had been wanting to write my own Swift Iterator
for some time now, so decided this was the best time to do so.
An Iterator
is basically just a type that can iterate over some elements. Those elements could come from an Array
, Set
, Dictionary
or some other Collection
type.
For example, an
IndexingIterator
basically iterates over the indexes in a collection.
In Swift 4 the only requirement for defining your own Iterator
is to conform to IteratorProtocol
as well as defining a single function next()
– which is responsible for returning the next element.
InfiniteIterator
What I decided to build was something I’ve called an InfiniteIterator
which basically iterates over the elements of a collection, but when it reaches the end, it starts from the beginning again – infinitely!
public struct InfiniteIterator<Base: Collection>: IteratorProtocol {
public mutating func next() -> Base.Iterator.Element? {
// see playground for full source
}
}
Using this approach, I was able to simply iterate over the indexPaths
for the items in my UICollectionViewLayout
, grab the next frame in my layout and then assign that to the attributes.frame
property.
public final class ArticleLayout: UICollectionViewLayout {
public override func prepare() {
// ...
var frames = InfiniteIterator(collection: layout.frame(for: element, in: collectionView.bounds))
// ...
for section in 0..<collectionView.numberOfSections {
for item in 0..<collectionView.numberOfItems(inSection: section) {
guard let frame = frames.next() else { fatalError() }
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
attributes.frame = frame
}
}
}
}
Summary
I’m really pleased with the result. I was able to define the primary pattern of my layout, and then simply iterate
over the elements of that layout to generate my feed.
Iterators are very powerful abstractions that provide much more powerful ways of stepping through our collections.
Its unlikely that you’ll ever need to create one yourself since the Swift Standard Library provides more than enough for daily use.
However, if you’re looking for an exercise how about writing an iterator that returns every 2nd element from a collection 🙂
If you want to share your own creations with me you can find me on Twitter as @shaps or post in the comments.