Fundamentals 12 min read

Understanding Swift Sequence and Collection Protocols: From Basics to Advanced Types

This article explains Swift's protocol‑oriented collection system, covering the Sequence and IteratorProtocol basics, demonstrating custom infinite sequences, exploring type‑erasing wrappers like AnySequence and AnyIterator, and detailing the Collection hierarchy including Collection, BidirectionalCollection, RandomAccessCollection, MutableCollection and RangeReplaceableCollection.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
Understanding Swift Sequence and Collection Protocols: From Basics to Advanced Types

Swift is a protocol‑oriented language where protocols can be extended to provide default implementations. The article begins by introducing the Sequence protocol, which requires an associatedtype Iterator and a makeIterator() method that returns an iterator conforming to IteratorProtocol with a mutating next() method.

Example code shows a simple InfiniteIterator that always returns the same value, and an InfiniteSequence that creates such an iterator. Using prefix on the sequence demonstrates iteration:

protocol Sequence {
    associatedtype Iterator
    func makeIterator() -> Iterator
}

protocol IteratorProtocol {
    associatedtype Element
    mutating func next() -> Element?
}
struct InfiniteIterator: IteratorProtocol {
    let value: Int
    mutating func next() -> Int? { return value }
}

struct InfiniteSequence: Sequence {
    let value: Int
    func makeIterator() -> InfiniteIterator { return InfiniteIterator(value: value) }
}

let infinite = InfiniteSequence(value: 20)
for value in infinite.prefix(5) {
    print(value) // 20
}

The article then introduces AnySequence , a type‑erasing wrapper that forwards operations to an underlying sequence, simplifying sequence creation:

func sequence
(first: T, next: @escaping (T) -> T?) -> UnfoldFirstSequence
func infiniteBasic(value: Int) -> UnfoldSequence
{
    return sequence(first: value) { _ in value }
}

Using AnySequence the same infinite sequence can be expressed more concisely:

func infinite(value: Int) -> AnySequence
{
    return AnySequence {
        sequence(first: value) { _ in value }
    }
}

for value in infinite(value: 24).prefix(5) {
    print(value) // 24
}

Next, the article covers AnyIterator , which hides the iterator implementation behind a closure:

func infinite(value: Int) -> AnySequence
{
    return AnySequence {
        AnyIterator { value }
    }
}

A more elaborate example uses a defer block to increment an index and stop after a limit:

var x = 0
func infinite2(value: Int) -> AnySequence
{
    return AnySequence {
        AnyIterator {
            defer { x += 1 }
            return x < 15 ? x : nil
        }
    }
}

The article then moves to the Collection protocol, which adds an indexable, finite sequence capability. A concrete FizzBuzz collection is presented, defining startIndex , endIndex , index(after:) , and a subscript that returns "Fizz", "Buzz", "FizzBuzz" or the number as a string.

struct FizzBuzz: Collection {
    typealias Index = Int
    var startIndex: Index { return 1 }
    var endIndex: Index { return 101 }
    func index(after i: Index) -> Index { return i + 1 }
    subscript(index: Index) -> String {
        precondition(indices.contains(index), "out of 1-100")
        switch (index.isMultiple(of: 3), index.isMultiple(of: 5)) {
        case (false, false): return String(index)
        case (true, false):  return "Fizz"
        case (false, true):  return "Buzz"
        case (true, true):   return "FizzBuzz"
        }
    }
}

for value in FizzBuzz() {
    print(value)
}

Further, the article explains the hierarchy of collection protocols: BidirectionalCollection adds index(before:) for reverse traversal, RandomAccessCollection enables O(1) indexed access, MutableCollection allows element mutation via a writable subscript, and RangeReplaceableCollection supports inserting and removing arbitrary sub‑ranges.

Finally, a short code snippet shows how to obtain the last element of any collection efficiently using index(before:) :

var last: Iterator.Element? {
    guard !self.isEmpty else { return nil }
    let indexOfLastItem = self.index(before: self.endIndex)
    return self[indexOfLastItem]
}

The article concludes that Sequence and Collection form the foundation of Swift's collection types, while the specialized protocols give developers fine‑grained control over functionality and performance.

SwiftIteratorProtocolSequenceAnySequenceBidirectionalCollectionCollection
Sohu Tech Products
Written by

Sohu Tech Products

A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.