IGListKit/docs/getting-started.html
Ryan Nystrom f2a9d93be8 Add details for 1.0 to 2.0 migration
Summary:
Added migration guide for `IGListDiffable` changes.

Closes #221
Closes https://github.com/Instagram/IGListKit/pull/274

Differential Revision: D4265446

Pulled By: rnystrom

fbshipit-source-id: b98a975a9ef65bca0d46a1011e76834ecf08c681
2016-12-02 06:58:59 -08:00

354 lines
25 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<title>Getting Started Reference</title>
<link rel="stylesheet" type="text/css" href="css/jazzy.css" />
<link rel="stylesheet" type="text/css" href="css/highlight.css" />
<meta charset='utf-8'>
<script src="js/jquery.min.js" defer></script>
<script src="js/jazzy.js" defer></script>
</head>
<body>
<a title="Getting Started Reference"></a>
<header>
<div class="content-wrapper">
<p><a href="index.html">IGListKit Docs</a> (100% documented)</p>
<p class="header-right"><a href="https://github.com/Instagram/IGListKit"><img src="img/gh.png"/>View on GitHub</a></p>
</div>
</header>
<div class="content-wrapper">
<p id="breadcrumbs">
<a href="index.html">IGListKit Reference</a>
<img id="carat" src="img/carat.png" />
Getting Started Reference
</p>
</div>
<div class="content-wrapper">
<nav class="sidebar">
<ul class="nav-groups">
<li class="nav-group-name">
<a href="Guides.html">Guides</a>
<ul class="nav-group-tasks">
<li class="nav-group-task">
<a href="getting-started.html">Getting Started</a>
</li>
<li class="nav-group-task">
<a href="iglistdiffable-and-equality.html">IGListDiffable and Equality</a>
</li>
<li class="nav-group-task">
<a href="migration.html">Migration</a>
</li>
</ul>
</li>
<li class="nav-group-name">
<a href="Categories.html">Categories</a>
<ul class="nav-group-tasks">
<li class="nav-group-task">
<a href="Categories.html#/c:objc(cy)NSNumber@IGListDiffable">NSNumber(IGListDiffable)</a>
</li>
<li class="nav-group-task">
<a href="Categories.html#/c:objc(cy)NSString@IGListDiffable">NSString(IGListDiffable)</a>
</li>
</ul>
</li>
<li class="nav-group-name">
<a href="Classes.html">Classes</a>
<ul class="nav-group-tasks">
<li class="nav-group-task">
<a href="Classes/IGListAdapter.html">IGListAdapter</a>
</li>
<li class="nav-group-task">
<a href="Classes/IGListAdapterUpdater.html">IGListAdapterUpdater</a>
</li>
<li class="nav-group-task">
<a href="Classes/IGListBatchUpdateData.html">IGListBatchUpdateData</a>
</li>
<li class="nav-group-task">
<a href="Classes.html#/c:objc(cs)IGListCollectionView">IGListCollectionView</a>
</li>
<li class="nav-group-task">
<a href="Classes/IGListGridCollectionViewLayout.html">IGListGridCollectionViewLayout</a>
</li>
<li class="nav-group-task">
<a href="Classes/IGListIndexPathResult.html">IGListIndexPathResult</a>
</li>
<li class="nav-group-task">
<a href="Classes/IGListIndexSetResult.html">IGListIndexSetResult</a>
</li>
<li class="nav-group-task">
<a href="Classes/IGListMoveIndex.html">IGListMoveIndex</a>
</li>
<li class="nav-group-task">
<a href="Classes/IGListMoveIndexPath.html">IGListMoveIndexPath</a>
</li>
<li class="nav-group-task">
<a href="Classes.html#/c:objc(cs)IGListReloadDataUpdater">IGListReloadDataUpdater</a>
</li>
<li class="nav-group-task">
<a href="Classes/IGListSectionController.html">IGListSectionController</a>
</li>
<li class="nav-group-task">
<a href="Classes/IGListSingleSectionController.html">IGListSingleSectionController</a>
</li>
<li class="nav-group-task">
<a href="Classes/IGListStackedSectionController.html">IGListStackedSectionController</a>
</li>
</ul>
</li>
<li class="nav-group-name">
<a href="Constants.html">Constants</a>
<ul class="nav-group-tasks">
<li class="nav-group-task">
<a href="Constants.html#/c:@IGListKitVersionNumber">IGListKitVersionNumber</a>
</li>
<li class="nav-group-task">
<a href="Constants.html#/c:@IGListKitVersionString">IGListKitVersionString</a>
</li>
</ul>
</li>
<li class="nav-group-name">
<a href="Enums.html">Enums</a>
<ul class="nav-group-tasks">
<li class="nav-group-task">
<a href="Enums/IGListDiffOption.html">IGListDiffOption</a>
</li>
<li class="nav-group-task">
<a href="Enums/IGListExperiment.html">IGListExperiment</a>
</li>
</ul>
</li>
<li class="nav-group-name">
<a href="Protocols.html">Protocols</a>
<ul class="nav-group-tasks">
<li class="nav-group-task">
<a href="Protocols/IGListAdapterDataSource.html">IGListAdapterDataSource</a>
</li>
<li class="nav-group-task">
<a href="Protocols/IGListAdapterDelegate.html">IGListAdapterDelegate</a>
</li>
<li class="nav-group-task">
<a href="Protocols/IGListAdapterUpdaterDelegate.html">IGListAdapterUpdaterDelegate</a>
</li>
<li class="nav-group-task">
<a href="Protocols/IGListCollectionContext.html">IGListCollectionContext</a>
</li>
<li class="nav-group-task">
<a href="Protocols/IGListDiffable.html">IGListDiffable</a>
</li>
<li class="nav-group-task">
<a href="Protocols/IGListDisplayDelegate.html">IGListDisplayDelegate</a>
</li>
<li class="nav-group-task">
<a href="Protocols/IGListScrollDelegate.html">IGListScrollDelegate</a>
</li>
<li class="nav-group-task">
<a href="Protocols/IGListSectionType.html">IGListSectionType</a>
</li>
<li class="nav-group-task">
<a href="Protocols/IGListSingleSectionControllerDelegate.html">IGListSingleSectionControllerDelegate</a>
</li>
<li class="nav-group-task">
<a href="Protocols/IGListSupplementaryViewSource.html">IGListSupplementaryViewSource</a>
</li>
<li class="nav-group-task">
<a href="Protocols/IGListUpdatingDelegate.html">IGListUpdatingDelegate</a>
</li>
<li class="nav-group-task">
<a href="Protocols/IGListWorkingRangeDelegate.html">IGListWorkingRangeDelegate</a>
</li>
</ul>
</li>
<li class="nav-group-name">
<a href="Type Definitions.html">Type Definitions</a>
<ul class="nav-group-tasks">
<li class="nav-group-task">
<a href="Type Definitions.html#/c:IGListUpdatingDelegate.h@T@IGListItemUpdateBlock">IGListItemUpdateBlock</a>
</li>
<li class="nav-group-task">
<a href="Type Definitions.html#/c:IGListUpdatingDelegate.h@T@IGListObjectTransitionBlock">IGListObjectTransitionBlock</a>
</li>
<li class="nav-group-task">
<a href="Type Definitions.html#/c:IGListUpdatingDelegate.h@T@IGListReloadUpdateBlock">IGListReloadUpdateBlock</a>
</li>
<li class="nav-group-task">
<a href="Type Definitions.html#/c:IGListSingleSectionController.h@T@IGListSingleSectionCellConfigureBlock">IGListSingleSectionCellConfigureBlock</a>
</li>
<li class="nav-group-task">
<a href="Type Definitions.html#/c:IGListSingleSectionController.h@T@IGListSingleSectionCellSizeBlock">IGListSingleSectionCellSizeBlock</a>
</li>
<li class="nav-group-task">
<a href="Type Definitions.html#/c:IGListAdapter.h@T@IGListUpdaterCompletion">IGListUpdaterCompletion</a>
</li>
<li class="nav-group-task">
<a href="Type Definitions.html#/c:IGListUpdatingDelegate.h@T@IGListUpdatingCompletion">IGListUpdatingCompletion</a>
</li>
</ul>
</li>
<li class="nav-group-name">
<a href="Functions.html">Functions</a>
<ul class="nav-group-tasks">
<li class="nav-group-task">
<a href="Functions.html#/c:@F@IGListDiff">IGListDiff</a>
</li>
<li class="nav-group-task">
<a href="Functions.html#/c:@F@IGListDiffExperiment">IGListDiffExperiment</a>
</li>
<li class="nav-group-task">
<a href="Functions.html#/c:@F@IGListDiffPaths">IGListDiffPaths</a>
</li>
<li class="nav-group-task">
<a href="Functions.html#/c:@F@IGListDiffPathsExperiment">IGListDiffPathsExperiment</a>
</li>
<li class="nav-group-task">
<a href="Functions.html#/c:IGListExperiments.h@F@IGListExperimentEnabled">IGListExperimentEnabled</a>
</li>
</ul>
</li>
</ul>
</nav>
<article class="main-content">
<section>
<section class="section">
<a href='#getting-started' class='anchor' aria-hidden=true><span class="header-anchor"></span></a><h1 id='getting-started'>Getting Started</h1>
<p>This guide provides a brief overview for how to get started using <code>IGListKit</code>.</p>
<a href='#creating-your-first-list' class='anchor' aria-hidden=true><span class="header-anchor"></span></a><h2 id='creating-your-first-list'>Creating your first list</h2>
<p>After installing <code>IGListKit</code>, creating a new list is easy.</p>
<a href='#creating-a-section-controller' class='anchor' aria-hidden=true><span class="header-anchor"></span></a><h3 id='creating-a-section-controller'>Creating a section controller</h3>
<p>Creating a new section controller is simple. You subclass <code>IGListSectionController</code> and conform to the <code>IGListSectionType</code> protocol. Once you conform to <code>IGListSectionType</code>, the compiler will make sure you implement all of the required methods.</p>
<p>Take a look at <a href="https://raw.githubusercontent.com/Instagram/IGListKit/master/Examples/Examples-iOS/IGListKitExamples/SectionControllers/LabelSectionController.swift">LabelSectionController</a> for an example section controller that handles a <code>String</code> and configures a single cell with a <code>UILabel</code>.</p>
<pre class="highlight swift"><code><span class="kd">class</span> <span class="kt">LabelSectionController</span><span class="p">:</span> <span class="kt">IGListSectionController</span><span class="p">,</span> <span class="kt">IGListSectionType</span> <span class="p">{</span>
<span class="c1">// ...</span>
<span class="p">}</span>
</code></pre>
<a href='#creating-the-ui' class='anchor' aria-hidden=true><span class="header-anchor"></span></a><h3 id='creating-the-ui'>Creating the UI</h3>
<p>After creating at least one section controller, you must create an <code>IGListCollectionView</code> and <code>IGListAdapter</code>.</p>
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">layout</span> <span class="o">=</span> <span class="kt">UICollectionViewFlowLayout</span><span class="p">()</span>
<span class="k">let</span> <span class="nv">collectionView</span> <span class="o">=</span> <span class="kt">IGListCollectionView</span><span class="p">(</span><span class="nv">frame</span><span class="p">:</span> <span class="o">.</span><span class="n">zero</span><span class="p">,</span> <span class="nv">collectionViewLayout</span><span class="p">:</span> <span class="n">layout</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">updater</span> <span class="o">=</span> <span class="kt">IGListAdapterUpdater</span><span class="p">()</span>
<span class="k">let</span> <span class="nv">adapter</span> <span class="o">=</span> <span class="kt">IGListAdapter</span><span class="p">(</span><span class="nv">updater</span><span class="p">:</span> <span class="n">updater</span><span class="p">,</span> <span class="nv">viewController</span><span class="p">:</span> <span class="k">self</span><span class="p">,</span> <span class="nv">workingRangeSize</span><span class="p">:</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">adapter</span><span class="o">.</span><span class="n">collectionView</span> <span class="o">=</span> <span class="n">collectionView</span>
</code></pre>
<blockquote>
<p><strong>Note:</strong> This example is done within a <code>UIViewController</code> and uses both a stock <code>UICollectionViewFlowLayout</code> and <code>IGListAdapterUpdater</code>. You can use your own layout and updater if you need advanced features!</p>
</blockquote>
<a href='#connecting-the-data-source' class='anchor' aria-hidden=true><span class="header-anchor"></span></a><h3 id='connecting-the-data-source'>Connecting the data source</h3>
<p>The last step is the <code>IGListAdapter</code>&rsquo;s data source and returning some data.</p>
<pre class="highlight swift"><code><span class="kd">func</span> <span class="nf">objects</span><span class="p">(</span><span class="k">for</span> <span class="nv">listAdapter</span><span class="p">:</span> <span class="kt">IGListAdapter</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="kt">IGListDiffable</span><span class="p">]</span> <span class="p">{</span>
<span class="c1">// this can be anything!</span>
<span class="k">return</span> <span class="p">[</span> <span class="s">"Foo"</span><span class="p">,</span> <span class="s">"Bar"</span><span class="p">,</span> <span class="mi">42</span><span class="p">,</span> <span class="s">"Biz"</span> <span class="p">]</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">listAdapter</span><span class="p">(</span><span class="n">_</span> <span class="nv">listAdapter</span><span class="p">:</span> <span class="kt">IGListAdapter</span><span class="p">,</span> <span class="n">sectionControllerFor</span> <span class="nv">object</span><span class="p">:</span> <span class="kt">Any</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">IGListSectionController</span> <span class="p">{</span>
<span class="k">if</span> <span class="n">object</span> <span class="k">is</span> <span class="kt">String</span> <span class="p">{</span>
<span class="k">return</span> <span class="kt">LabelSectionController</span><span class="p">()</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">return</span> <span class="kt">NumberSectionController</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">emptyView</span><span class="p">(</span><span class="k">for</span> <span class="nv">listAdapter</span><span class="p">:</span> <span class="kt">IGListAdapter</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">UIView</span><span class="p">?</span> <span class="p">{</span>
<span class="k">return</span> <span class="kc">nil</span>
<span class="p">}</span>
</code></pre>
<p>You can return an array of <em>any</em> type of data, as long as it conforms to <code>IGListDiffable</code>.</p>
<a href='#diffing' class='anchor' aria-hidden=true><span class="header-anchor"></span></a><h2 id='diffing'>Diffing</h2>
<p><code>IGListKit</code> uses an algorithm adapted from a paper titled <a href="http://dl.acm.org/citation.cfm?id=359467&amp;dl=ACM&amp;coll=DL">A technique for isolating differences between files</a> by Paul Heckel. This algorithm uses a technique known as the <em>longest common subsequence</em> to find a minimal diff between collections in linear time <code>O(n)</code>. It finds all <strong>inserts</strong>, <strong>deletes</strong>, <strong>updates</strong>, and <strong>moves</strong> between arrays of data.</p>
<p>To add custom, diffable models, you need to conform to the <code>IGListDiffable</code> protocol and implement <code>diffIdentifier()</code> and <code>isEqual(toDiffableObject:)</code>.</p>
<p>For an example, consider the following model:</p>
<pre class="highlight swift"><code><span class="kd">class</span> <span class="kt">User</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">primaryKey</span><span class="p">:</span> <span class="kt">Int</span>
<span class="k">let</span> <span class="nv">name</span><span class="p">:</span> <span class="kt">String</span>
<span class="c1">// implementation, etc</span>
<span class="p">}</span>
</code></pre>
<p>The user&rsquo;s <code>primaryKey</code> uniquely identifies user data, and the <code>name</code> is just the value for that user.</p>
<p>Let&rsquo;s say a server returns a <code>User</code> object that looks like this:</p>
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">shayne</span> <span class="o">=</span> <span class="kt">User</span><span class="p">(</span><span class="nv">primaryKey</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="nv">name</span><span class="p">:</span> <span class="s">"Shayne"</span><span class="p">)</span>
</code></pre>
<p>But sometime after the client receives <code>shayne</code>, someone changes their name:</p>
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">ann</span> <span class="o">=</span> <span class="kt">User</span><span class="p">(</span><span class="nv">primaryKey</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="nv">name</span><span class="p">:</span> <span class="s">"Ann"</span><span class="p">)</span>
</code></pre>
<p>Both <code>shayne</code> and <code>ann</code> represent the same <em>unique</em> data because they share the same <code>primaryKey</code>, but they are not <em>equal</em> because their names are different.</p>
<p>To represent this in <code>IGListKit</code>&rsquo;s diffing, add and implement the <code>IGListDiffable</code> protocol:</p>
<pre class="highlight swift"><code><span class="kd">extension</span> <span class="kt">User</span><span class="p">:</span> <span class="kt">IGListDiffable</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">diffIdentifier</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="kt">NSObjectProtocol</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">primaryKey</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">isEqual</span><span class="p">(</span><span class="n">toDiffableObject</span> <span class="nv">object</span><span class="p">:</span> <span class="kt">Any</span><span class="p">?)</span> <span class="o">-&gt;</span> <span class="kt">Bool</span> <span class="p">{</span>
<span class="k">if</span> <span class="k">let</span> <span class="nv">object</span> <span class="o">=</span> <span class="n">object</span> <span class="k">as?</span> <span class="kt">User</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">name</span> <span class="o">==</span> <span class="n">object</span><span class="o">.</span><span class="n">name</span>
<span class="p">}</span>
<span class="k">return</span> <span class="kc">false</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre>
<p>The algorithm will skip updating two <code>User</code> objects that have the same <code>primaryKey</code> and <code>name</code>, even if they are different instances! You now avoid unnecessary UI updates in the collection view even when providing new instances.</p>
<blockquote>
<p><strong>Note:</strong> Remember that <code>isEqual(toDiffableObject:)</code> should return <code>false</code> when you want to reload the cells in the corresponding section controller.</p>
</blockquote>
<a href='#diffing-outside-of-iglistkit' class='anchor' aria-hidden=true><span class="header-anchor"></span></a><h3 id='diffing-outside-of-iglistkit'>Diffing outside of IGListKit</h3>
<p>If you want to use the diffing algorithm outside of <code>IGListAdapter</code> and <code>UICollectionView</code>, you can! The diffing algorithm was built with the flexibility to be used with any models that conform to <code>IGListDiffable</code>.</p>
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">result</span> <span class="o">=</span> <span class="kt">IGListDiff</span><span class="p">(</span><span class="n">oldUsers</span><span class="p">,</span> <span class="n">newUsers</span><span class="p">,</span> <span class="o">.</span><span class="n">equality</span><span class="p">)</span>
</code></pre>
<p>With this you have all of the deletes, reloads, moves, and inserts! There&rsquo;s even a function to generate <code>NSIndexPath</code> results.</p>
<a href='#advanced-features' class='anchor' aria-hidden=true><span class="header-anchor"></span></a><h2 id='advanced-features'>Advanced Features</h2>
<a href='#working-range' class='anchor' aria-hidden=true><span class="header-anchor"></span></a><h3 id='working-range'>Working Range</h3>
<p>A <em>working range</em> is a range of section controllers who aren&rsquo;t yet visible, but are near the screen. Section controllers are notified of their entrance and exit to this range. This concept lets your section controllers <strong>prepare content</strong> before they come on screen (e.g. download images).</p>
<p>The <code>IGListAdapter</code> must be initialized with a range value in order to work. This value is a multiple of the visible height or width, depending on the scroll-direction.</p>
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">adapter</span> <span class="o">=</span> <span class="kt">IGListAdapter</span><span class="p">(</span><span class="nv">updater</span><span class="p">:</span> <span class="kt">IGListAdapterUpdater</span><span class="p">(),</span>
<span class="nv">viewController</span><span class="p">:</span> <span class="k">self</span><span class="p">,</span>
<span class="nv">workingRangeSize</span><span class="p">:</span> <span class="mi">1</span><span class="p">)</span> <span class="c1">// 1 before/after visible objects</span>
</code></pre>
<p><img src="https://raw.githubusercontent.com/Instagram/IGListKit/master/Resources/workingrange.png" alt="working-range"></p>
<p>You can set the weak <code>workingRangeDelegate</code> on a section controller to receive events.</p>
<a href='#supplementary-views' class='anchor' aria-hidden=true><span class="header-anchor"></span></a><h3 id='supplementary-views'>Supplementary Views</h3>
<p>Adding supplementary views to section controllers is as simple as setting the (weak) <code>supplementaryViewSource</code> and implementing the <code>IGListSupplementaryViewSource</code> protocol. This protocol works nearly the same as returning and configuring cells.</p>
<a href='#display-delegate' class='anchor' aria-hidden=true><span class="header-anchor"></span></a><h3 id='display-delegate'>Display Delegate</h3>
<p>Section controllers can set the weak <code>displayDelegate</code> delegate to an object, including <code>self</code>, to receive display events about a section controller and individual cells.</p>
<a href='#custom-updaters' class='anchor' aria-hidden=true><span class="header-anchor"></span></a><h3 id='custom-updaters'>Custom Updaters</h3>
<p>The default <code>IGListAdapterUpdater</code> should handle any <code>UICollectionView</code> update that you need. However, if you find the functionality lacking, or want to perform updates in a very specific way, you can create an object that conforms to the <code>IGListUpdatingDelegate</code> protocol and initialize a new <code>IGListAdapter</code> with it.</p>
<p>Check out the updater <code>IGListReloadDataUpdater</code> (used in unit tests) for an example.</p>
</section>
</section>
<section id="footer">
<p>&copy; 2016 <a class="link" href="https://twitter.com/fbOpenSource" target="_blank" rel="external">Instagram</a>. All rights reserved. (Last updated: 2016-12-02)</p>
<p>Generated by <a class="link" href="https://github.com/realm/jazzy" target="_blank" rel="external">jazzy ♪♫ v0.7.2</a>, a <a class="link" href="http://realm.io" target="_blank" rel="external">Realm</a> project.</p>
</section>
</article>
</div>
</body>
</div>
</html>