{"id":2726,"date":"2026-02-05T11:41:43","date_gmt":"2026-02-05T11:41:43","guid":{"rendered":"https:\/\/demo.materiamedica.net\/demo6\/?p=2726"},"modified":"2026-02-05T11:41:43","modified_gmt":"2026-02-05T11:41:43","slug":"chapter-70-swift-collection-protocols","status":"publish","type":"post","link":"https:\/\/demo.materiamedica.net\/demo6\/chapter-70-swift-collection-protocols\/","title":{"rendered":"Chapter 70: Swift Collection Protocols"},"content":{"rendered":"<h3 dir=\"auto\">1. Why does Swift have Collection Protocols? (the most important big picture)<\/h3>\n<p dir=\"auto\">Swift wants to let you write <strong>generic, reusable code<\/strong> that works with <strong>many different kinds of collections<\/strong> \u2014 not just Array.<\/p>\n<p dir=\"auto\">Examples of different kinds of collections:<\/p>\n<ul dir=\"auto\">\n<li>Array \u2013 ordered, allows duplicates<\/li>\n<li>Set \u2013 unordered, no duplicates<\/li>\n<li>Dictionary \u2013 key-value pairs (keys are unique)<\/li>\n<li>String \u2013 sequence of characters<\/li>\n<li>ArraySlice \u2013 view into part of an array<\/li>\n<li>Custom collections you write yourself<\/li>\n<li>Third-party types (e.g. from libraries)<\/li>\n<\/ul>\n<p dir=\"auto\">Instead of writing separate functions for each type:<\/p>\n<div dir=\"auto\">\n<div data-testid=\"code-block\">\n<div>\n<div>Swift<\/div>\n<div>\n<pre tabindex=\"0\"><code>func printAll(array: [String]) { \u2026 }\r\nfunc printAll(set: Set&lt;String&gt;) { \u2026 }\r\nfunc printAll(dictKeys: Dictionary&lt;String, Int&gt;.Keys) { \u2026 }<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p dir=\"auto\">Swift lets you write <strong>one generic function<\/strong> that works with <strong>all<\/strong> of them \u2014 as long as they conform to the right protocol.<\/p>\n<p dir=\"auto\">That is the whole reason for Collection protocols.<\/p>\n<h3 dir=\"auto\">2. The Main Collection Protocols \u2013 Hierarchy Overview<\/h3>\n<p dir=\"auto\">Here is the most important family tree you should keep in mind:<\/p>\n<div dir=\"auto\">\n<div data-testid=\"code-block\">\n<div>\n<div>text<\/div>\n<div>\n<pre tabindex=\"0\"><code>Sequence\r\n   \u2191\r\nCollection   \u2190 most important for everyday work\r\n   \u2191\r\nBidirectionalCollection\r\n   \u2191\r\nRandomAccessCollection   \u2190 Array, ArraySlice, ContiguousArray belong here<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p dir=\"auto\">You will mostly interact with these four:<\/p>\n<div>\n<div dir=\"auto\">\n<table dir=\"auto\">\n<thead>\n<tr>\n<th data-col-size=\"md\">Protocol<\/th>\n<th data-col-size=\"xs\">Ordered?<\/th>\n<th data-col-size=\"md\">Index type<\/th>\n<th data-col-size=\"xs\">Random access?<\/th>\n<th data-col-size=\"lg\">Typical types that conform<\/th>\n<th data-col-size=\"xl\">When you care about it<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td data-col-size=\"md\"><strong>Sequence<\/strong><\/td>\n<td data-col-size=\"xs\">Yes<\/td>\n<td data-col-size=\"md\">\u2014 (no index)<\/td>\n<td data-col-size=\"xs\">No<\/td>\n<td data-col-size=\"lg\">Array, Set, Dictionary, String, Range, StrideThrough\u2026<\/td>\n<td data-col-size=\"xl\">When you just iterate (for-in, forEach, map, filter\u2026)<\/td>\n<\/tr>\n<tr>\n<td data-col-size=\"md\"><strong>Collection<\/strong><\/td>\n<td data-col-size=\"xs\">Yes<\/td>\n<td data-col-size=\"md\">Index (opaque)<\/td>\n<td data-col-size=\"xs\">No (but fast in practice for Array)<\/td>\n<td data-col-size=\"lg\">Array, Set, Dictionary, String, ArraySlice\u2026<\/td>\n<td data-col-size=\"xl\">When you need .count, .isEmpty, subscript [index], .first, .last\u2026<\/td>\n<\/tr>\n<tr>\n<td data-col-size=\"md\"><strong>BidirectionalCollection<\/strong><\/td>\n<td data-col-size=\"xs\">Yes<\/td>\n<td data-col-size=\"md\">Supports going backward<\/td>\n<td data-col-size=\"xs\">No<\/td>\n<td data-col-size=\"lg\">Array, Set, Dictionary, String\u2026<\/td>\n<td data-col-size=\"xl\">When you need .last, .reversed(), iterate backward<\/td>\n<\/tr>\n<tr>\n<td data-col-size=\"md\"><strong>RandomAccessCollection<\/strong><\/td>\n<td data-col-size=\"xs\">Yes<\/td>\n<td data-col-size=\"md\">Supports fast jumping<\/td>\n<td data-col-size=\"xs\">Yes<\/td>\n<td data-col-size=\"lg\">Array, ArraySlice, ContiguousArray, Range\u2026<\/td>\n<td data-col-size=\"xl\">When you need fast random access coll[i] or slicing<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<div><\/div>\n<\/div>\n<\/div>\n<p dir=\"auto\"><strong>Rule of thumb used by most good Swift developers:<\/strong><\/p>\n<ul dir=\"auto\">\n<li>If you only <strong>iterate<\/strong> (for-in, map, filter, reduce, forEach) \u2192 Sequence is enough<\/li>\n<li>If you need <strong>count<\/strong>, <strong>subscript<\/strong>, <strong>first\/last<\/strong> \u2192 you usually want Collection<\/li>\n<li>If you need <strong>fast random access<\/strong> or slicing \u2192 RandomAccessCollection (Array is this)<\/li>\n<\/ul>\n<h3 dir=\"auto\">3. Sequence \u2013 the foundation (what almost everything conforms to)<\/h3>\n<div dir=\"auto\">\n<div data-testid=\"code-block\">\n<div>\n<div>Swift<\/div>\n<div>\n<pre tabindex=\"0\"><code>func printAllElements&lt;S: Sequence&gt;(_ items: S) where S.Element == String {\r\n    for item in items {\r\n        print(item)\r\n    }\r\n}\r\n\r\nlet names = [\"Rahul\", \"Priya\", \"Aarav\"]\r\nlet uniqueNames = Set(names)\r\nlet nameString = \"RahulPriyaAarav\"\r\n\r\nprintAllElements(names)         \/\/ Array\r\nprintAllElements(uniqueNames)   \/\/ Set\r\nprintAllElements(nameString)    \/\/ String<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p dir=\"auto\"><strong>Real-life use-case \u2013 generic logging function<\/strong><\/p>\n<div dir=\"auto\">\n<div data-testid=\"code-block\">\n<div>\n<div>Swift<\/div>\n<div>\n<pre tabindex=\"0\"><code>func logItems&lt;S: Sequence&gt;(_ items: S, prefix: String = \"\u2192\") where S.Element: CustomStringConvertible {\r\n    items.forEach { item in\r\n        print(\"\\(prefix) \\(item)\")\r\n    }\r\n}\r\n\r\nlogItems([1, 2, 3])                     \/\/ \u2192 1 \u2192 2 \u2192 3\r\nlogItems(Set([\"apple\", \"banana\"]))      \/\/ \u2192 apple \u2192 banana (order not guaranteed)\r\nlogItems(\"hello\".map { $0 })            \/\/ \u2192 h \u2192 e \u2192 l \u2192 l \u2192 o<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<h3 dir=\"auto\">4. Collection \u2013 when you need count, indices, subscript<\/h3>\n<div dir=\"auto\">\n<div data-testid=\"code-block\">\n<div>\n<div>Swift<\/div>\n<div>\n<pre tabindex=\"0\"><code>func printWithIndices&lt;C: Collection&gt;(_ items: C) where C.Element: CustomStringConvertible {\r\n    if items.isEmpty {\r\n        print(\"Empty collection\")\r\n        return\r\n    }\r\n    \r\n    print(\"Collection with \\(items.count) items:\")\r\n    \r\n    for (offset, element) in items.enumerated() {\r\n        print(\"  [\\(offset)] \\(element)\")\r\n    }\r\n}<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p dir=\"auto\">This function works with <strong>Array<\/strong>, <strong>Set<\/strong>, <strong>Dictionary.keys<\/strong>, <strong>String<\/strong>, <strong>ArraySlice<\/strong>, etc.<\/p>\n<p dir=\"auto\"><strong>Real-life example \u2013 safe slicing \/ pagination<\/strong><\/p>\n<div dir=\"auto\">\n<div data-testid=\"code-block\">\n<div>\n<div>Swift<\/div>\n<div>\n<pre tabindex=\"0\"><code>func getPage&lt;C: Collection&gt;(_ items: C, page: Int, pageSize: Int = 10) -&gt; C.SubSequence\r\n    where C.Index == Int {\r\n    \r\n    let start = page * pageSize\r\n    let end = min(start + pageSize, items.count)\r\n    \r\n    guard start &lt; items.count else {\r\n        return items[items.endIndex..&lt;items.endIndex] \/\/ empty slice\r\n    }\r\n    \r\n    return items[start..&lt;end]\r\n}<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<h3 dir=\"auto\">5. RandomAccessCollection \u2013 when you need fast random access<\/h3>\n<div dir=\"auto\">\n<div data-testid=\"code-block\">\n<div>\n<div>Swift<\/div>\n<div>\n<pre tabindex=\"0\"><code>func swapFirstAndLast&lt;C: RandomAccessCollection &amp; MutableCollection&gt;(_ collection: inout C) {\r\n    guard !collection.isEmpty else { return }\r\n    \r\n    \/\/ Because it's RandomAccessCollection, subscript is O(1)\r\n    let firstIndex = collection.startIndex\r\n    let lastIndex = collection.index(before: collection.endIndex)\r\n    \r\n    collection.swapAt(firstIndex, lastIndex)\r\n}<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div dir=\"auto\">\n<div data-testid=\"code-block\">\n<div>\n<div>Swift<\/div>\n<div>\n<pre tabindex=\"0\"><code>var numbers = [10, 20, 30, 40, 50]\r\nswapFirstAndLast(&amp;numbers)\r\nprint(numbers)     \/\/ [50, 20, 30, 40, 10]<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p dir=\"auto\"><strong>Array<\/strong>, ArraySlice, ContiguousArray conform to RandomAccessCollection. Set and Dictionary do <strong>not<\/strong> \u2014 their access is fast but <strong>not guaranteed O(1)<\/strong> for indexed access.<\/p>\n<h3 dir=\"auto\">6. Real-life examples you will actually write<\/h3>\n<h4 dir=\"auto\">Example 1 \u2013 Generic function that works on any Collection<\/h4>\n<div dir=\"auto\">\n<div data-testid=\"code-block\">\n<div>\n<div>Swift<\/div>\n<div>\n<pre tabindex=\"0\"><code>func printSummary&lt;C: Collection&gt;(_ items: C) where C.Element: CustomStringConvertible {\r\n    if items.isEmpty {\r\n        print(\"No items\")\r\n        return\r\n    }\r\n    \r\n    print(\"Showing \\(items.count) items:\")\r\n    \r\n    \/\/ Safe because Collection guarantees .first \/ .last\r\n    if let first = items.first, let last = items.last {\r\n        print(\"  First: \\(first)\")\r\n        print(\"  Last:  \\(last)\")\r\n    }\r\n    \r\n    \/\/ enumerated() works because Collection has indices\r\n    items.enumerated().forEach { index, item in\r\n        print(\"  #\\(index + 1): \\(item)\")\r\n    }\r\n}<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p dir=\"auto\">This one function works with Array, Set, Dictionary.keys, String, etc.<\/p>\n<h4 dir=\"auto\">Example 2 \u2013 Safe pagination \/ slicing<\/h4>\n<div dir=\"auto\">\n<div data-testid=\"code-block\">\n<div>\n<div>Swift<\/div>\n<div>\n<pre tabindex=\"0\"><code>func getPage&lt;C: Collection&gt;(_ items: C, page: Int, pageSize: Int = 20) -&gt; C.SubSequence\r\n    where C.Index == Int {\r\n    \r\n    let start = page * pageSize\r\n    guard start &lt; items.count else {\r\n        return items[items.endIndex..&lt;items.endIndex] \/\/ empty\r\n    }\r\n    \r\n    let end = min(start + pageSize, items.count)\r\n    return items[start..&lt;end]\r\n}<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<h3 dir=\"auto\">7. Quick Summary \u2013 Which protocol should you usually ask for?<\/h3>\n<div>\n<div dir=\"auto\">\n<table dir=\"auto\">\n<thead>\n<tr>\n<th data-col-size=\"md\">You need\u2026<\/th>\n<th data-col-size=\"sm\">Minimum protocol you should ask for<\/th>\n<th data-col-size=\"lg\">Why? \/ Typical generic signature<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td data-col-size=\"md\">Just iterate (for-in, forEach, map, filter)<\/td>\n<td data-col-size=\"sm\">Sequence<\/td>\n<td data-col-size=\"lg\">func process&lt;S: Sequence&gt;(_ items: S) where S.Element == String<\/td>\n<\/tr>\n<tr>\n<td data-col-size=\"md\">Need .count, .isEmpty, .first, .last, subscript<\/td>\n<td data-col-size=\"sm\">Collection<\/td>\n<td data-col-size=\"lg\">func summary&lt;C: Collection&gt;(_ items: C)<\/td>\n<\/tr>\n<tr>\n<td data-col-size=\"md\">Need fast random access \/ slicing<\/td>\n<td data-col-size=\"sm\">RandomAccessCollection<\/td>\n<td data-col-size=\"lg\">func swapFirstLast&lt;C: RandomAccessCollection &amp; MutableCollection&gt;(_ c: inout C)<\/td>\n<\/tr>\n<tr>\n<td data-col-size=\"md\">Need bidirectional walking (rare)<\/td>\n<td data-col-size=\"sm\">BidirectionalCollection<\/td>\n<td data-col-size=\"lg\">reversed(), backward iteration<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<div><\/div>\n<\/div>\n<\/div>\n<p dir=\"auto\"><strong>Rule of thumb used by most good Swift developers:<\/strong><\/p>\n<ul dir=\"auto\">\n<li>Start with <strong>Sequence<\/strong> \u2014 it\u2019s the most flexible<\/li>\n<li>Move to <strong>Collection<\/strong> when you need .count, indices, subscript<\/li>\n<li>Only ask for <strong>RandomAccessCollection<\/strong> when you <strong>really<\/strong> need fast random access or slicing<\/li>\n<\/ul>\n<h3 dir=\"auto\">8. Small Practice \u2013 Try these<\/h3>\n<ol dir=\"auto\">\n<li>Write a generic function that prints the first and last element of <strong>any<\/strong>Collection (use Collection constraint)<\/li>\n<li>Write a generic function that returns the number of elements <strong>and<\/strong> the first element (if exists) of any Sequence<\/li>\n<li>Write a function that takes any RandomAccessCollection and swaps the first and last element (only if there are at least 2 elements)<\/li>\n<\/ol>\n<p dir=\"auto\">Paste your code here if you want feedback or want to see cleaner versions!<\/p>\n<p dir=\"auto\">What would you like to explore next?<\/p>\n<ul dir=\"auto\">\n<li><strong>Sequence<\/strong> vs <strong>Collection<\/strong> vs <strong>RandomAccessCollection<\/strong> in depth<\/li>\n<li>Writing your own <strong>custom collection type<\/strong><\/li>\n<li>Collection protocols in <strong>SwiftUI<\/strong> (ForEach, List, data sources)<\/li>\n<li><strong>Lazy collections<\/strong> (lazy, LazySequence, LazyMapCollection\u2026)<\/li>\n<li>Or move to another topic (optionals, switch, functions, mutability\u2026)<\/li>\n<\/ul>\n<p dir=\"auto\">Just tell me \u2014 we\u2019ll continue in the same clear, patient, detailed style \ud83d\ude0a<\/p>\n","protected":false},"excerpt":{"rendered":"<p>1. Why does Swift have Collection Protocols? (the most important big picture) Swift wants to let you write generic, reusable code that works with many different kinds of collections \u2014 not just Array. Examples&#46;&#46;&#46;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[76],"tags":[],"class_list":["post-2726","post","type-post","status-publish","format-standard","hentry","category-swift"],"_links":{"self":[{"href":"https:\/\/demo.materiamedica.net\/demo6\/wp-json\/wp\/v2\/posts\/2726","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/demo.materiamedica.net\/demo6\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/demo.materiamedica.net\/demo6\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/demo.materiamedica.net\/demo6\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/demo.materiamedica.net\/demo6\/wp-json\/wp\/v2\/comments?post=2726"}],"version-history":[{"count":1,"href":"https:\/\/demo.materiamedica.net\/demo6\/wp-json\/wp\/v2\/posts\/2726\/revisions"}],"predecessor-version":[{"id":2727,"href":"https:\/\/demo.materiamedica.net\/demo6\/wp-json\/wp\/v2\/posts\/2726\/revisions\/2727"}],"wp:attachment":[{"href":"https:\/\/demo.materiamedica.net\/demo6\/wp-json\/wp\/v2\/media?parent=2726"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/demo.materiamedica.net\/demo6\/wp-json\/wp\/v2\/categories?post=2726"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/demo.materiamedica.net\/demo6\/wp-json\/wp\/v2\/tags?post=2726"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}