Tomasz Szulc iOS Software Engineer. Swift. Objective-C. Programming. Tips. Tricks. http://szulctomasz.com/ Tue, 09 May 2017 09:00:30 +0200 Tue, 09 May 2017 09:00:30 +0200 Jekyll v3.3.1 Asynchronous NSBlockOperation <p>Here is an useful <a href="https://gist.github.com/tomkowz/2734cf25318b7cfcd475b1149ab3ee7a">snippet</a> for asynchronous <code class="highlighter-rouge">NSBlockOperation</code>.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">class</span> <span class="kt">AsyncBlockOperation</span><span class="p">:</span> <span class="kt">NSOperation</span> <span class="p">{</span> <span class="kd">typealias</span> <span class="kt">Block</span> <span class="o">=</span> <span class="p">(</span><span class="kt">Void</span> <span class="o">-&gt;</span> <span class="kt">Void</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Void</span> <span class="kd">private</span> <span class="k">let</span> <span class="nv">block</span><span class="p">:</span> <span class="kt">Block</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">_executing</span> <span class="o">=</span> <span class="kc">false</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">_finished</span> <span class="o">=</span> <span class="kc">false</span> <span class="nf">init</span><span class="p">(</span><span class="nv">block</span><span class="p">:</span> <span class="kt">Block</span><span class="p">)</span> <span class="p">{</span> <span class="k">self</span><span class="o">.</span><span class="n">block</span> <span class="o">=</span> <span class="n">block</span> <span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">()</span> <span class="p">}</span> <span class="k">override</span> <span class="kd">func</span> <span class="nf">start</span><span class="p">()</span> <span class="p">{</span> <span class="nf">guard</span> <span class="p">(</span><span class="k">self</span><span class="o">.</span><span class="n">executing</span> <span class="o">||</span> <span class="k">self</span><span class="o">.</span><span class="n">cancelled</span><span class="p">)</span> <span class="o">==</span> <span class="kc">false</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span> <span class="k">self</span><span class="o">.</span><span class="n">executing</span> <span class="o">=</span> <span class="kc">true</span> <span class="k">self</span><span class="o">.</span><span class="nf">block</span><span class="p">(</span><span class="n">finish</span><span class="p">)</span> <span class="p">}</span> <span class="kd">private</span> <span class="kd">func</span> <span class="nf">finish</span><span class="p">()</span> <span class="p">{</span> <span class="k">self</span><span class="o">.</span><span class="n">executing</span> <span class="o">=</span> <span class="kc">false</span> <span class="k">self</span><span class="o">.</span><span class="n">finished</span> <span class="o">=</span> <span class="kc">true</span> <span class="p">}</span> <span class="k">override</span> <span class="k">var</span> <span class="nv">asynchronous</span><span class="p">:</span> <span class="kt">Bool</span> <span class="p">{</span> <span class="k">return</span> <span class="kc">true</span> <span class="p">}</span> <span class="k">override</span> <span class="k">var</span> <span class="nv">executing</span><span class="p">:</span> <span class="kt">Bool</span> <span class="p">{</span> <span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="n">_executing</span> <span class="p">}</span> <span class="k">set</span> <span class="p">{</span> <span class="k">let</span> <span class="nv">key</span> <span class="o">=</span> <span class="s">"isExecuting"</span> <span class="nf">willChangeValueForKey</span><span class="p">(</span><span class="n">key</span><span class="p">)</span> <span class="n">_executing</span> <span class="o">=</span> <span class="n">newValue</span> <span class="nf">didChangeValueForKey</span><span class="p">(</span><span class="n">key</span><span class="p">)</span> <span class="p">}</span> <span class="p">}</span> <span class="k">override</span> <span class="k">var</span> <span class="nv">finished</span><span class="p">:</span> <span class="kt">Bool</span> <span class="p">{</span> <span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="n">_finished</span> <span class="p">}</span> <span class="k">set</span> <span class="p">{</span> <span class="k">let</span> <span class="nv">key</span> <span class="o">=</span> <span class="s">"isFinished"</span> <span class="nf">willChangeValueForKey</span><span class="p">(</span><span class="n">key</span><span class="p">)</span> <span class="n">_finished</span> <span class="o">=</span> <span class="n">newValue</span> <span class="nf">didChangeValueForKey</span><span class="p">(</span><span class="n">key</span><span class="p">)</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>Usage:</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">let</span> <span class="nv">op</span> <span class="o">=</span> <span class="kt">AsyncBlockOperation</span> <span class="p">{</span> <span class="n">completion</span> <span class="k">in</span> <span class="nf">print</span><span class="p">(</span><span class="s">"Szulc Tomasz"</span><span class="p">)</span> <span class="nf">completion</span><span class="p">()</span> <span class="p">}</span></code></pre></figure> Tue, 09 May 2017 00:00:00 +0200 http://szulctomasz.com/2017/05/09/asynchronous-nsblockoperation.html http://szulctomasz.com/2017/05/09/asynchronous-nsblockoperation.html ios, swift, operation, async How do I build a Network Layer <p>Working and leading two projects at a time means a great opportunity to experiment with app architecture, and do experiments with other concepts I had in mind or just learned and wanted to try them out. One of topics I learned recently and I think you might find it useful if how I build a network layer now.</p> <p>Nowadays mobile apps are client-server oriented, so pretty much there is a network layer somewhere in the app, smaller or bigger. I saw many implementations to date but every had some drawbacks. Not thinking the latest one I build has no drawbacks, but it seems to work very well in two projects I am working on currently. <em>And has test coverage close to 100%</em>.</p> <p>In this article we’ll cover network layer that talks only with one backend, sending JSON encoded requests, so not that complex. The layer will talk with AWS later on and send some files there, but it should be easy to extend its functionality to do so.</p> <h3 id="thinking-process">Thinking process</h3> <p>Here are some questions I like to ask myself before building such a layer.</p> <ul> <li>Where to put the code that have knowledge about the backend url?</li> <li>Where to put the code that knows about the endpoints?</li> <li>Where to put the code that knows how to build a request?</li> <li>Where to keep all the code that cares about preparing parameters for a request?</li> <li>Where should I store authentication token?</li> <li>How to execute requests?</li> <li>When and where to execute requests?</li> <li>Do I care about cancelling requests?</li> <li>Do I need to care about wrong backend responses? Some backend bugs?</li> <li>Do I need to use 3rd party frameworks? What frameworks should I use?</li> <li>Is there any core data stuff passing around?</li> <li>How to test the solution?</li> </ul> <h3 id="storing-backend-url">Storing backend url</h3> <p>First of all, <em>where should I put the backend url?</em> How other piece of the system will know where to send requests? I prefer to create a <code class="highlighter-rouge">BackendConfiguration</code> class that stores such information.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">import</span> <span class="kt">Foundation</span> <span class="kd">public</span> <span class="kd">final</span> <span class="kd">class</span> <span class="kt">BackendConfiguration</span> <span class="p">{</span> <span class="k">let</span> <span class="nv">baseURL</span><span class="p">:</span> <span class="kt">NSURL</span> <span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="nv">baseURL</span><span class="p">:</span> <span class="kt">NSURL</span><span class="p">)</span> <span class="p">{</span> <span class="k">self</span><span class="o">.</span><span class="n">baseURL</span> <span class="o">=</span> <span class="n">baseURL</span> <span class="p">}</span> <span class="kd">public</span> <span class="kd">static</span> <span class="k">var</span> <span class="nv">shared</span><span class="p">:</span> <span class="kt">BackendConfiguration</span><span class="o">!</span> <span class="p">}</span></code></pre></figure> <p>Easy to test, easy to configure. You can set the <code class="highlighter-rouge">shared</code> static variable and access it wherever you want in your network layer, no need to pass it everywhere.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">let</span> <span class="nv">backendURL</span> <span class="o">=</span> <span class="kt">NSURL</span><span class="p">(</span><span class="nv">string</span><span class="p">:</span> <span class="s">"https://szulctomasz.com"</span><span class="p">)</span><span class="o">!</span> <span class="kt">BackendConfiguration</span><span class="o">.</span><span class="n">shared</span> <span class="o">=</span> <span class="kt">BackendConfiguration</span><span class="p">(</span><span class="nv">baseURL</span><span class="p">:</span> <span class="n">backendURL</span><span class="p">)</span></code></pre></figure> <h3 id="endpoints">Endpoints</h3> <p>That’s the topic I experimented with for a while before I found a ready-to-go solution. Tried hardcoding endpoints while configuring <code class="highlighter-rouge">NSURLSession</code>, tried some dummy <code class="highlighter-rouge">Resource</code>-like objects that knows about the endpoint and can be easily instantiated and injected, but it was still not what I was looking for.</p> <p>I came up with idea to create <code class="highlighter-rouge">*Request</code> object that knows which endpoint to hit, what method to use, should it be GET, POST, PUT or different, how to configure the body of a request and what headers to pass.</p> <p>This is what I came up with</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">protocol</span> <span class="kt">BackendAPIRequest</span> <span class="p">{</span> <span class="k">var</span> <span class="nv">endpoint</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span> <span class="k">var</span> <span class="nv">method</span><span class="p">:</span> <span class="kt">NetworkService</span><span class="o">.</span><span class="kt">Method</span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span> <span class="k">var</span> <span class="nv">parameters</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">AnyObject</span><span class="p">]?</span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span> <span class="k">var</span> <span class="nv">headers</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">String</span><span class="p">]?</span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>A class that implements the protocol is able to provide a basic informations that are required to build a request. The <code class="highlighter-rouge">NetworkService.Method</code> is just an enum with <code class="highlighter-rouge">GET</code>, <code class="highlighter-rouge">POST</code>, <code class="highlighter-rouge">PUT</code>, <code class="highlighter-rouge">DELETE</code> cases.</p> <p>An example request that maps an endpoint might look like this</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">final</span> <span class="kd">class</span> <span class="kt">SignUpRequest</span><span class="p">:</span> <span class="kt">BackendAPIRequest</span> <span class="p">{</span> <span class="kd">private</span> <span class="k">let</span> <span class="nv">firstName</span><span class="p">:</span> <span class="kt">String</span> <span class="kd">private</span> <span class="k">let</span> <span class="nv">lastName</span><span class="p">:</span> <span class="kt">String</span> <span class="kd">private</span> <span class="k">let</span> <span class="nv">email</span><span class="p">:</span> <span class="kt">String</span> <span class="kd">private</span> <span class="k">let</span> <span class="nv">password</span><span class="p">:</span> <span class="kt">String</span> <span class="nf">init</span><span class="p">(</span><span class="nv">firstName</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">lastName</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">email</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">password</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="p">{</span> <span class="k">self</span><span class="o">.</span><span class="n">firstName</span> <span class="o">=</span> <span class="n">firstName</span> <span class="k">self</span><span class="o">.</span><span class="n">lastName</span> <span class="o">=</span> <span class="n">lastName</span> <span class="k">self</span><span class="o">.</span><span class="n">email</span> <span class="o">=</span> <span class="n">email</span> <span class="k">self</span><span class="o">.</span><span class="n">password</span> <span class="o">=</span> <span class="n">password</span> <span class="p">}</span> <span class="k">var</span> <span class="nv">endpoint</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span> <span class="k">return</span> <span class="s">"/users"</span> <span class="p">}</span> <span class="k">var</span> <span class="nv">method</span><span class="p">:</span> <span class="kt">NetworkService</span><span class="o">.</span><span class="kt">Method</span> <span class="p">{</span> <span class="k">return</span> <span class="o">.</span><span class="kt">POST</span> <span class="p">}</span> <span class="k">var</span> <span class="nv">parameters</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">AnyObject</span><span class="p">]?</span> <span class="p">{</span> <span class="k">return</span> <span class="p">[</span> <span class="s">"first_name"</span><span class="p">:</span> <span class="n">firstName</span><span class="p">,</span> <span class="s">"last_name"</span><span class="p">:</span> <span class="n">lastName</span><span class="p">,</span> <span class="s">"email"</span><span class="p">:</span> <span class="n">email</span><span class="p">,</span> <span class="s">"password"</span><span class="p">:</span> <span class="n">password</span> <span class="p">]</span> <span class="p">}</span> <span class="k">var</span> <span class="nv">headers</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">String</span><span class="p">]?</span> <span class="p">{</span> <span class="k">return</span> <span class="p">[</span><span class="s">"Content-Type"</span><span class="p">:</span> <span class="s">"application/json"</span><span class="p">]</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>To not create the dictionary for headers everywhere we can define extension for <code class="highlighter-rouge">BackendAPIRequest</code>.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">extension</span> <span class="kt">BackendAPIRequest</span> <span class="p">{</span> <span class="kd">func</span> <span class="nf">defaultJSONHeaders</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">String</span><span class="p">]</span> <span class="p">{</span> <span class="k">return</span> <span class="p">[</span><span class="s">"Content-Type"</span><span class="p">:</span> <span class="s">"application/json"</span><span class="p">]</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>The <code class="highlighter-rouge">*Request</code> class takes all the parameters needed to make a successful request. You’re always sure that at least all the parameters you need will be passed, otherwise you can’t create a request object.</p> <p>Defining endpoint is easy. If there should be some id of an object that should be included in the endpoint it is super easy to add it because you actually would have such id stored as a property.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">private</span> <span class="k">let</span> <span class="nv">id</span><span class="p">:</span> <span class="kt">String</span> <span class="nf">init</span><span class="p">(</span><span class="nv">id</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="o">...</span><span class="p">)</span> <span class="p">{</span> <span class="k">self</span><span class="o">.</span><span class="n">id</span> <span class="o">=</span> <span class="n">id</span> <span class="p">}</span> <span class="k">var</span> <span class="nv">endpoint</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span> <span class="k">return</span> <span class="s">"/users/</span><span class="se">\(</span><span class="n">id</span><span class="se">)</span><span class="s">"</span> <span class="p">}</span></code></pre></figure> <p>The method of the request never changes, the parameters body is easily constructed and very easy to maintain, headers too. Everything is very easy to test.</p> <h3 id="executing-the-request">Executing the request</h3> <p><em>Do I need any 3rd party frameworks to communicate with the backend?</em></p> <p>I see that people are using AFNetworking (Objective-C) and Alamofire for Swift. I used it many times, but for some time I am not using it. Since we’ve got <code class="highlighter-rouge">NSURLSession</code> that do its job very well I don’t think you need any 3rd party framework. IMO this dependency is going to make your app architecture more complex.</p> <p>The current solution consists of two classes - <code class="highlighter-rouge">NetworkService</code> and <code class="highlighter-rouge">BackendService</code>.</p> <ul> <li> <p><code class="highlighter-rouge">NetworkService</code> - allows you to execute HTTP request, it incorporates <code class="highlighter-rouge">NSURLSession</code> internally. Every network service can execute just one request at a time, can cancel the request (big advantage), and has callbacks for success and failure responses.</p> </li> <li> <p><code class="highlighter-rouge">BackendService</code> - (Not the coolest name ever but fits quite well) is the class that takes requests (<code class="highlighter-rouge">*Request</code> objects described above) related to the backend. It uses <code class="highlighter-rouge">NetworkService</code> internally. In the current version I am using, it tries to serialize the response data to json using <code class="highlighter-rouge">NSJSONSerializer</code>.</p> </li> </ul> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">class</span> <span class="kt">NetworkService</span> <span class="p">{</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">task</span><span class="p">:</span> <span class="kt">NSURLSessionDataTask</span><span class="p">?</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">successCodes</span><span class="p">:</span> <span class="kt">Range</span><span class="o">&lt;</span><span class="kt">Int</span><span class="o">&gt;</span> <span class="o">=</span> <span class="mi">200</span><span class="o">..&lt;</span><span class="mi">299</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">failureCodes</span><span class="p">:</span> <span class="kt">Range</span><span class="o">&lt;</span><span class="kt">Int</span><span class="o">&gt;</span> <span class="o">=</span> <span class="mi">400</span><span class="o">..&lt;</span><span class="mi">499</span> <span class="kd">enum</span> <span class="kt">Method</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span> <span class="k">case</span> <span class="kt">GET</span><span class="p">,</span> <span class="kt">POST</span><span class="p">,</span> <span class="kt">PUT</span><span class="p">,</span> <span class="kt">DELETE</span> <span class="p">}</span> <span class="kd">func</span> <span class="nf">request</span><span class="p">(</span><span class="n">url</span> <span class="nv">url</span><span class="p">:</span> <span class="kt">NSURL</span><span class="p">,</span> <span class="nv">method</span><span class="p">:</span> <span class="kt">Method</span><span class="p">,</span> <span class="nv">params</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">AnyObject</span><span class="p">]?</span> <span class="o">=</span> <span class="kc">nil</span><span class="p">,</span> <span class="nv">headers</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">String</span><span class="p">]?</span> <span class="o">=</span> <span class="kc">nil</span><span class="p">,</span> <span class="nv">success</span><span class="p">:</span> <span class="p">(</span><span class="kt">NSData</span><span class="p">?</span> <span class="o">-&gt;</span> <span class="kt">Void</span><span class="p">)?</span> <span class="o">=</span> <span class="kc">nil</span><span class="p">,</span> <span class="nv">failure</span><span class="p">:</span> <span class="p">((</span><span class="nv">data</span><span class="p">:</span> <span class="kt">NSData</span><span class="p">?,</span> <span class="nv">error</span><span class="p">:</span> <span class="kt">NSError</span><span class="p">?,</span> <span class="nv">responseCode</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Void</span><span class="p">)?</span> <span class="o">=</span> <span class="kc">nil</span><span class="p">)</span> <span class="p">{</span> <span class="k">let</span> <span class="nv">mutableRequest</span> <span class="o">=</span> <span class="kt">NSMutableURLRequest</span><span class="p">(</span><span class="kt">URL</span><span class="p">:</span> <span class="n">url</span><span class="p">,</span> <span class="nv">cachePolicy</span><span class="p">:</span> <span class="o">.</span><span class="kt">ReloadIgnoringLocalAndRemoteCacheData</span><span class="p">,</span> <span class="nv">timeoutInterval</span><span class="p">:</span> <span class="mf">10.0</span><span class="p">)</span> <span class="n">mutableRequest</span><span class="o">.</span><span class="n">allHTTPHeaderFields</span> <span class="o">=</span> <span class="n">headers</span> <span class="n">mutableRequest</span><span class="o">.</span><span class="kt">HTTPMethod</span> <span class="o">=</span> <span class="n">method</span><span class="o">.</span><span class="n">rawValue</span> <span class="k">if</span> <span class="k">let</span> <span class="nv">params</span> <span class="o">=</span> <span class="n">params</span> <span class="p">{</span> <span class="n">mutableRequest</span><span class="o">.</span><span class="kt">HTTPBody</span> <span class="o">=</span> <span class="k">try!</span> <span class="kt">NSJSONSerialization</span><span class="o">.</span><span class="nf">dataWithJSONObject</span><span class="p">(</span><span class="n">params</span><span class="p">,</span> <span class="nv">options</span><span class="p">:</span> <span class="p">[])</span> <span class="p">}</span> <span class="k">let</span> <span class="nv">session</span> <span class="o">=</span> <span class="kt">NSURLSession</span><span class="o">.</span><span class="nf">sharedSession</span><span class="p">()</span> <span class="n">task</span> <span class="o">=</span> <span class="n">session</span><span class="o">.</span><span class="nf">dataTaskWithRequest</span><span class="p">(</span><span class="n">mutableRequest</span><span class="p">,</span> <span class="nv">completionHandler</span><span class="p">:</span> <span class="p">{</span> <span class="n">data</span><span class="p">,</span> <span class="n">response</span><span class="p">,</span> <span class="n">error</span> <span class="k">in</span> <span class="c1">// Decide whether the response is success or failure and call</span> <span class="c1">// proper callback.</span> <span class="p">})</span> <span class="n">task</span><span class="p">?</span><span class="o">.</span><span class="nf">resume</span><span class="p">()</span> <span class="p">}</span> <span class="kd">func</span> <span class="nf">cancel</span><span class="p">()</span> <span class="p">{</span> <span class="n">task</span><span class="p">?</span><span class="o">.</span><span class="nf">cancel</span><span class="p">()</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">class</span> <span class="kt">BackendService</span> <span class="p">{</span> <span class="kd">private</span> <span class="k">let</span> <span class="nv">conf</span><span class="p">:</span> <span class="kt">BackendConfiguration</span> <span class="kd">private</span> <span class="k">let</span> <span class="nv">service</span><span class="p">:</span> <span class="kt">NetworkService</span><span class="o">!</span> <span class="nf">init</span><span class="p">(</span><span class="n">_</span> <span class="nv">conf</span><span class="p">:</span> <span class="kt">BackendConfiguration</span><span class="p">)</span> <span class="p">{</span> <span class="k">self</span><span class="o">.</span><span class="n">conf</span> <span class="o">=</span> <span class="n">conf</span> <span class="k">self</span><span class="o">.</span><span class="n">service</span> <span class="o">=</span> <span class="kt">NetworkService</span><span class="p">()</span> <span class="p">}</span> <span class="kd">func</span> <span class="nf">request</span><span class="p">(</span><span class="nv">request</span><span class="p">:</span> <span class="kt">BackendAPIRequest</span><span class="p">,</span> <span class="nv">success</span><span class="p">:</span> <span class="p">(</span><span class="kt">AnyObject</span><span class="p">?</span> <span class="o">-&gt;</span> <span class="kt">Void</span><span class="p">)?</span> <span class="o">=</span> <span class="kc">nil</span><span class="p">,</span> <span class="nv">failure</span><span class="p">:</span> <span class="p">(</span><span class="kt">NSError</span> <span class="o">-&gt;</span> <span class="kt">Void</span><span class="p">)?</span> <span class="o">=</span> <span class="kc">nil</span><span class="p">)</span> <span class="p">{</span> <span class="k">let</span> <span class="nv">url</span> <span class="o">=</span> <span class="n">conf</span><span class="o">.</span><span class="n">baseURL</span><span class="o">.</span><span class="kt">URLByAppendingPathComponent</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">endpoint</span><span class="p">)</span> <span class="k">var</span> <span class="nv">headers</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">headers</span> <span class="c1">// Set authentication token if available.</span> <span class="n">headers</span><span class="p">?[</span><span class="s">"X-Api-Auth-Token"</span><span class="p">]</span> <span class="o">=</span> <span class="kt">BackendAuth</span><span class="o">.</span><span class="n">shared</span><span class="o">.</span><span class="n">token</span> <span class="n">service</span><span class="o">.</span><span class="nf">request</span><span class="p">(</span><span class="nv">url</span><span class="p">:</span> <span class="n">url</span><span class="p">,</span> <span class="nv">method</span><span class="p">:</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span><span class="p">,</span> <span class="nv">params</span><span class="p">:</span> <span class="n">request</span><span class="o">.</span><span class="n">parameters</span><span class="p">,</span> <span class="nv">headers</span><span class="p">:</span> <span class="n">headers</span><span class="p">,</span> <span class="nv">success</span><span class="p">:</span> <span class="p">{</span> <span class="n">data</span> <span class="k">in</span> <span class="k">var</span> <span class="nv">json</span><span class="p">:</span> <span class="kt">AnyObject</span><span class="p">?</span> <span class="o">=</span> <span class="kc">nil</span> <span class="k">if</span> <span class="k">let</span> <span class="nv">data</span> <span class="o">=</span> <span class="n">data</span> <span class="p">{</span> <span class="n">json</span> <span class="o">=</span> <span class="k">try</span><span class="p">?</span> <span class="kt">NSJSONSerialization</span><span class="o">.</span><span class="kt">JSONObjectWithData</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="nv">options</span><span class="p">:</span> <span class="p">[])</span> <span class="p">}</span> <span class="nf">success</span><span class="p">?(</span><span class="n">json</span><span class="p">)</span> <span class="p">},</span> <span class="nv">failure</span><span class="p">:</span> <span class="p">{</span> <span class="n">data</span><span class="p">,</span> <span class="n">error</span><span class="p">,</span> <span class="n">statusCode</span> <span class="k">in</span> <span class="c1">// Do stuff you need, and call failure block.</span> <span class="p">})</span> <span class="p">}</span> <span class="kd">func</span> <span class="nf">cancel</span><span class="p">()</span> <span class="p">{</span> <span class="n">service</span><span class="o">.</span><span class="nf">cancel</span><span class="p">()</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>As you can see the <code class="highlighter-rouge">BackendService</code> can set authentication token in headers. The <code class="highlighter-rouge">BackendAuth</code> objects is a simple storage that stores the token in <code class="highlighter-rouge">NSUserDefaults</code>. If that would be necessary it could be storing the token in Keychain.</p> <p>The <code class="highlighter-rouge">BackendService</code> takes <code class="highlighter-rouge">BackendAPIRequest</code> as a parameter of <code class="highlighter-rouge">request(_:success:failure:)</code> method and extracts necessary informations from the <code class="highlighter-rouge">request</code> object. This is nicely encapsulated and backend service just consumes what it gets.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">public</span> <span class="kd">final</span> <span class="kd">class</span> <span class="kt">BackendAuth</span> <span class="p">{</span> <span class="kd">private</span> <span class="k">let</span> <span class="nv">key</span> <span class="o">=</span> <span class="s">"BackendAuthToken"</span> <span class="kd">private</span> <span class="k">let</span> <span class="nv">defaults</span><span class="p">:</span> <span class="kt">NSUserDefaults</span> <span class="kd">public</span> <span class="kd">static</span> <span class="k">var</span> <span class="nv">shared</span><span class="p">:</span> <span class="kt">BackendAuth</span><span class="o">!</span> <span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="n">defaults</span><span class="p">:</span> <span class="kt">NSUserDefaults</span><span class="p">)</span> <span class="p">{</span> <span class="k">self</span><span class="o">.</span><span class="n">defaults</span> <span class="o">=</span> <span class="n">defaults</span> <span class="p">}</span> <span class="kd">public</span> <span class="kd">func</span> <span class="nf">setToken</span><span class="p">(</span><span class="nv">token</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="p">{</span> <span class="n">defaults</span><span class="o">.</span><span class="nf">setValue</span><span class="p">(</span><span class="n">token</span><span class="p">,</span> <span class="nv">forKey</span><span class="p">:</span> <span class="n">key</span><span class="p">)</span> <span class="p">}</span> <span class="kd">public</span> <span class="k">var</span> <span class="nv">token</span><span class="p">:</span> <span class="kt">String</span><span class="p">?</span> <span class="p">{</span> <span class="k">return</span> <span class="n">defaults</span><span class="o">.</span><span class="nf">valueForKey</span><span class="p">(</span><span class="n">key</span><span class="p">)</span> <span class="k">as?</span> <span class="kt">String</span> <span class="p">}</span> <span class="kd">public</span> <span class="kd">func</span> <span class="nf">deleteToken</span><span class="p">()</span> <span class="p">{</span> <span class="n">defaults</span><span class="o">.</span><span class="nf">removeObjectForKey</span><span class="p">(</span><span class="n">key</span><span class="p">)</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>Both <code class="highlighter-rouge">NetworkService</code>, <code class="highlighter-rouge">BackendService</code> and <code class="highlighter-rouge">BackendAuth</code> are easy to test and maintain.</p> <h3 id="queueing-requests">Queueing requests</h3> <p>Several questions to cover here. <em>What way would we like to perform network requests? What if we want to perform many requests at a time? How would we like to be notified about the success or failure for requests in general?</em></p> <p>Decided to go with <code class="highlighter-rouge">NSOperationQueue</code> and <code class="highlighter-rouge">NSOperation</code>s that execute network requests.</p> <p>So, I subclassed <code class="highlighter-rouge">NSOperation</code> and overrided its <code class="highlighter-rouge">asynchronous</code> property to return <code class="highlighter-rouge">true</code>.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">public</span> <span class="kd">class</span> <span class="kt">NetworkOperation</span><span class="p">:</span> <span class="kt">NSOperation</span> <span class="p">{</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">_ready</span><span class="p">:</span> <span class="kt">Bool</span> <span class="kd">public</span> <span class="k">override</span> <span class="k">var</span> <span class="nv">ready</span><span class="p">:</span> <span class="kt">Bool</span> <span class="p">{</span> <span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="n">_ready</span> <span class="p">}</span> <span class="k">set</span> <span class="p">{</span> <span class="nf">update</span><span class="p">({</span> <span class="k">self</span><span class="o">.</span><span class="n">_ready</span> <span class="o">=</span> <span class="n">newValue</span> <span class="p">},</span> <span class="nv">key</span><span class="p">:</span> <span class="s">"isReady"</span><span class="p">)</span> <span class="p">}</span> <span class="p">}</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">_executing</span><span class="p">:</span> <span class="kt">Bool</span> <span class="kd">public</span> <span class="k">override</span> <span class="k">var</span> <span class="nv">executing</span><span class="p">:</span> <span class="kt">Bool</span> <span class="p">{</span> <span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="n">_executing</span> <span class="p">}</span> <span class="k">set</span> <span class="p">{</span> <span class="nf">update</span><span class="p">({</span> <span class="k">self</span><span class="o">.</span><span class="n">_executing</span> <span class="o">=</span> <span class="n">newValue</span> <span class="p">},</span> <span class="nv">key</span><span class="p">:</span> <span class="s">"isExecuting"</span><span class="p">)</span> <span class="p">}</span> <span class="p">}</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">_finished</span><span class="p">:</span> <span class="kt">Bool</span> <span class="kd">public</span> <span class="k">override</span> <span class="k">var</span> <span class="nv">finished</span><span class="p">:</span> <span class="kt">Bool</span> <span class="p">{</span> <span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="n">_finished</span> <span class="p">}</span> <span class="k">set</span> <span class="p">{</span> <span class="nf">update</span><span class="p">({</span> <span class="k">self</span><span class="o">.</span><span class="n">_finished</span> <span class="o">=</span> <span class="n">newValue</span> <span class="p">},</span> <span class="nv">key</span><span class="p">:</span> <span class="s">"isFinished"</span><span class="p">)</span> <span class="p">}</span> <span class="p">}</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">_cancelled</span><span class="p">:</span> <span class="kt">Bool</span> <span class="kd">public</span> <span class="k">override</span> <span class="k">var</span> <span class="nv">cancelled</span><span class="p">:</span> <span class="kt">Bool</span> <span class="p">{</span> <span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="n">_cancelled</span> <span class="p">}</span> <span class="k">set</span> <span class="p">{</span> <span class="nf">update</span><span class="p">({</span> <span class="k">self</span><span class="o">.</span><span class="n">_cancelled</span> <span class="o">=</span> <span class="n">newValue</span> <span class="p">},</span> <span class="nv">key</span><span class="p">:</span> <span class="s">"isCancelled"</span><span class="p">)</span> <span class="p">}</span> <span class="p">}</span> <span class="kd">private</span> <span class="kd">func</span> <span class="nf">update</span><span class="p">(</span><span class="nv">change</span><span class="p">:</span> <span class="kt">Void</span> <span class="o">-&gt;</span> <span class="kt">Void</span><span class="p">,</span> <span class="nv">key</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="p">{</span> <span class="nf">willChangeValueForKey</span><span class="p">(</span><span class="n">key</span><span class="p">)</span> <span class="nf">change</span><span class="p">()</span> <span class="nf">didChangeValueForKey</span><span class="p">(</span><span class="n">key</span><span class="p">)</span> <span class="p">}</span> <span class="k">override</span> <span class="nf">init</span><span class="p">()</span> <span class="p">{</span> <span class="n">_ready</span> <span class="o">=</span> <span class="kc">true</span> <span class="n">_executing</span> <span class="o">=</span> <span class="kc">false</span> <span class="n">_finished</span> <span class="o">=</span> <span class="kc">false</span> <span class="n">_cancelled</span> <span class="o">=</span> <span class="kc">false</span> <span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">()</span> <span class="n">name</span> <span class="o">=</span> <span class="s">"Network Operation"</span> <span class="p">}</span> <span class="kd">public</span> <span class="k">override</span> <span class="k">var</span> <span class="nv">asynchronous</span><span class="p">:</span> <span class="kt">Bool</span> <span class="p">{</span> <span class="k">return</span> <span class="kc">true</span> <span class="p">}</span> <span class="kd">public</span> <span class="k">override</span> <span class="kd">func</span> <span class="nf">start</span><span class="p">()</span> <span class="p">{</span> <span class="k">if</span> <span class="k">self</span><span class="o">.</span><span class="n">executing</span> <span class="o">==</span> <span class="kc">false</span> <span class="p">{</span> <span class="k">self</span><span class="o">.</span><span class="n">ready</span> <span class="o">=</span> <span class="kc">false</span> <span class="k">self</span><span class="o">.</span><span class="n">executing</span> <span class="o">=</span> <span class="kc">true</span> <span class="k">self</span><span class="o">.</span><span class="n">finished</span> <span class="o">=</span> <span class="kc">false</span> <span class="k">self</span><span class="o">.</span><span class="n">cancelled</span> <span class="o">=</span> <span class="kc">false</span> <span class="p">}</span> <span class="p">}</span> <span class="c1">/// Used only by subclasses. Externally you should use `cancel`.</span> <span class="kd">func</span> <span class="nf">finish</span><span class="p">()</span> <span class="p">{</span> <span class="k">self</span><span class="o">.</span><span class="n">executing</span> <span class="o">=</span> <span class="kc">false</span> <span class="k">self</span><span class="o">.</span><span class="n">finished</span> <span class="o">=</span> <span class="kc">true</span> <span class="p">}</span> <span class="kd">public</span> <span class="k">override</span> <span class="kd">func</span> <span class="nf">cancel</span><span class="p">()</span> <span class="p">{</span> <span class="k">self</span><span class="o">.</span><span class="n">executing</span> <span class="o">=</span> <span class="kc">false</span> <span class="k">self</span><span class="o">.</span><span class="n">cancelled</span> <span class="o">=</span> <span class="kc">true</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>Next, because I want to use the <code class="highlighter-rouge">BackendService</code> for executing network calls I subclassed <code class="highlighter-rouge">NetworkOperation</code> and created <code class="highlighter-rouge">ServiceOperation</code>.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">public</span> <span class="kd">class</span> <span class="kt">ServiceOperation</span><span class="p">:</span> <span class="kt">NetworkOperation</span> <span class="p">{</span> <span class="k">let</span> <span class="nv">service</span><span class="p">:</span> <span class="kt">BackendService</span> <span class="kd">public</span> <span class="k">override</span> <span class="nf">init</span><span class="p">()</span> <span class="p">{</span> <span class="k">self</span><span class="o">.</span><span class="n">service</span> <span class="o">=</span> <span class="kt">BackendService</span><span class="p">(</span><span class="kt">BackendConfiguration</span><span class="o">.</span><span class="n">shared</span><span class="p">)</span> <span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">()</span> <span class="p">}</span> <span class="kd">public</span> <span class="k">override</span> <span class="kd">func</span> <span class="nf">cancel</span><span class="p">()</span> <span class="p">{</span> <span class="n">service</span><span class="o">.</span><span class="nf">cancel</span><span class="p">()</span> <span class="k">super</span><span class="o">.</span><span class="nf">cancel</span><span class="p">()</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>The class creates <code class="highlighter-rouge">BackendService</code> internally so I don’t need to create it in its every subclass.</p> <p>Here is how the <em>Sign In</em> operation might look like:</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">public</span> <span class="kd">class</span> <span class="kt">SignInOperation</span><span class="p">:</span> <span class="kt">ServiceOperation</span> <span class="p">{</span> <span class="kd">private</span> <span class="k">let</span> <span class="nv">request</span><span class="p">:</span> <span class="kt">SignInRequest</span> <span class="kd">public</span> <span class="k">var</span> <span class="nv">success</span><span class="p">:</span> <span class="p">(</span><span class="kt">SignInItem</span> <span class="o">-&gt;</span> <span class="kt">Void</span><span class="p">)?</span> <span class="kd">public</span> <span class="k">var</span> <span class="nv">failure</span><span class="p">:</span> <span class="p">(</span><span class="kt">NSError</span> <span class="o">-&gt;</span> <span class="kt">Void</span><span class="p">)?</span> <span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="nv">email</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">password</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="p">{</span> <span class="n">request</span> <span class="o">=</span> <span class="kt">SignInRequest</span><span class="p">(</span><span class="nv">email</span><span class="p">:</span> <span class="n">email</span><span class="p">,</span> <span class="nv">password</span><span class="p">:</span> <span class="n">password</span><span class="p">)</span> <span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">()</span> <span class="p">}</span> <span class="kd">public</span> <span class="k">override</span> <span class="kd">func</span> <span class="nf">start</span><span class="p">()</span> <span class="p">{</span> <span class="k">super</span><span class="o">.</span><span class="nf">start</span><span class="p">()</span> <span class="n">service</span><span class="o">.</span><span class="nf">request</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="nv">success</span><span class="p">:</span> <span class="n">handleSuccess</span><span class="p">,</span> <span class="nv">failure</span><span class="p">:</span> <span class="n">handleFailure</span><span class="p">)</span> <span class="p">}</span> <span class="kd">private</span> <span class="kd">func</span> <span class="nf">handleSuccess</span><span class="p">(</span><span class="nv">response</span><span class="p">:</span> <span class="kt">AnyObject</span><span class="p">?)</span> <span class="p">{</span> <span class="k">do</span> <span class="p">{</span> <span class="k">let</span> <span class="nv">item</span> <span class="o">=</span> <span class="k">try</span> <span class="kt">SignInResponseMapper</span><span class="o">.</span><span class="nf">process</span><span class="p">(</span><span class="n">response</span><span class="p">)</span> <span class="k">self</span><span class="o">.</span><span class="nf">success</span><span class="p">?(</span><span class="n">item</span><span class="p">)</span> <span class="k">self</span><span class="o">.</span><span class="nf">finish</span><span class="p">()</span> <span class="p">}</span> <span class="k">catch</span> <span class="p">{</span> <span class="nf">handleFailure</span><span class="p">(</span><span class="kt">NSError</span><span class="o">.</span><span class="nf">cannotParseResponse</span><span class="p">())</span> <span class="p">}</span> <span class="p">}</span> <span class="kd">private</span> <span class="kd">func</span> <span class="nf">handleFailure</span><span class="p">(</span><span class="nv">error</span><span class="p">:</span> <span class="kt">NSError</span><span class="p">)</span> <span class="p">{</span> <span class="k">self</span><span class="o">.</span><span class="nf">failure</span><span class="p">?(</span><span class="n">error</span><span class="p">)</span> <span class="k">self</span><span class="o">.</span><span class="nf">finish</span><span class="p">()</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>In the <code class="highlighter-rouge">start</code> method the service executes request that is created internally in the operation’s constructor. <code class="highlighter-rouge">handleSuccess</code> and <code class="highlighter-rouge">handleFailure</code> methods are passed as a callbacks for <code class="highlighter-rouge">request(_:success:failure:)</code> method of a service. IMO this makes the code more clean, and it is still readable.</p> <p>Operations are passed to a <code class="highlighter-rouge">NetworkQueue</code> object that is a singleton and can queue every operation. For now I keep it as simple as possible:</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">public</span> <span class="kd">class</span> <span class="kt">NetworkQueue</span> <span class="p">{</span> <span class="kd">public</span> <span class="kd">static</span> <span class="k">var</span> <span class="nv">shared</span><span class="p">:</span> <span class="kt">NetworkQueue</span><span class="o">!</span> <span class="k">let</span> <span class="nv">queue</span> <span class="o">=</span> <span class="kt">NSOperationQueue</span><span class="p">()</span> <span class="kd">public</span> <span class="nf">init</span><span class="p">()</span> <span class="p">{}</span> <span class="kd">public</span> <span class="kd">func</span> <span class="nf">addOperation</span><span class="p">(</span><span class="nv">op</span><span class="p">:</span> <span class="kt">NSOperation</span><span class="p">)</span> <span class="p">{</span> <span class="n">queue</span><span class="o">.</span><span class="nf">addOperation</span><span class="p">(</span><span class="n">op</span><span class="p">)</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>What are advantages of executing operations in one place?</p> <ul> <li>Easily cancellation of all network operations.</li> <li>Cancellation all operations that are downloading images or other operations that are requesting data that is not needed to provide user a basic experience of using the app while network connection is weak. E.g. you would like to prevent downloading images when user is on weak connection.</li> <li>You can build a priority queue and execute some requests first to get answer faster.</li> </ul> <h3 id="working-with-core-data">Working with Core Data</h3> <p>This is the aspect for which I had to delay the publication of this entry. In previous version of the network layer operations returned Core Data objects. The response was received, parsed and converted to Core Data object. This solution was far from ideal.</p> <ul> <li>The operation have to know what Core Data is. Because I had model detached to separate framework and network layer was in separate framework too, the network framework have to know about model framework.</li> <li>Each operation have to take additional <code class="highlighter-rouge">NSManagedObjectContext</code> parameter to know which context it should operate on.</li> <li>Each time the response was received and was about to call success block it first tried to find object in a context, or hit the disk to fetch object from disk. IMO this is a big disadvantage. You not always want to create Core Data object.</li> </ul> <p>So I came with idea to take out Core Data out of network layer completely. I created middle layer that are objects created in result of parsing responses.</p> <ul> <li>This way the parsing and creating objects is quick and not require to hit a disk.</li> <li>You also don’t need to pass <code class="highlighter-rouge">NSManagedObjectContext</code> to operation.</li> <li>You can update your core data object in the <code class="highlighter-rouge">success</code> block using the parsed item and reference to Core Data object that you probably keep somewhere where you create the operation - this is my case in most situations when the operation is added to a queue.</li> </ul> <h3 id="mapping-responses">Mapping responses</h3> <p>The idea of response mappers was to separate the logic of parsing and mapping JSON to useful items.</p> <p>We can distinguish two type of parsers. The first type return just a single object of specific type. The second type is a parser that parses array of such items.</p> <p>First, let’s define a common protocol for all the items:</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">public</span> <span class="kd">protocol</span> <span class="kt">ParsedItem</span> <span class="p">{}</span></code></pre></figure> <p>Now, here are some objects that are products of mappers:</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">public</span> <span class="kd">struct</span> <span class="kt">SignInItem</span><span class="p">:</span> <span class="kt">ParsedItem</span> <span class="p">{</span> <span class="kd">public</span> <span class="k">let</span> <span class="nv">token</span><span class="p">:</span> <span class="kt">String</span> <span class="kd">public</span> <span class="k">let</span> <span class="nv">uniqueId</span><span class="p">:</span> <span class="kt">String</span> <span class="p">}</span> <span class="kd">public</span> <span class="kd">struct</span> <span class="kt">UserItem</span><span class="p">:</span> <span class="kt">ParsedItem</span> <span class="p">{</span> <span class="kd">public</span> <span class="k">let</span> <span class="nv">uniqueId</span><span class="p">:</span> <span class="kt">String</span> <span class="kd">public</span> <span class="k">let</span> <span class="nv">firstName</span><span class="p">:</span> <span class="kt">String</span> <span class="kd">public</span> <span class="k">let</span> <span class="nv">lastName</span><span class="p">:</span> <span class="kt">String</span> <span class="kd">public</span> <span class="k">let</span> <span class="nv">email</span><span class="p">:</span> <span class="kt">String</span> <span class="kd">public</span> <span class="k">let</span> <span class="nv">phoneNumber</span><span class="p">:</span> <span class="kt">String</span><span class="p">?</span> <span class="p">}</span></code></pre></figure> <p>Let’s define an error type that will be thrown when something went wrong with parsing.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">internal</span> <span class="kd">enum</span> <span class="kt">ResponseMapperError</span><span class="p">:</span> <span class="kt">ErrorType</span> <span class="p">{</span> <span class="k">case</span> <span class="kt">Invalid</span> <span class="k">case</span> <span class="kt">MissingAttribute</span> <span class="p">}</span></code></pre></figure> <ul> <li><code class="highlighter-rouge">Invalid</code> - thrown when passed json is nil and should not be nil, or when it is array of objects instead of expected json with a single object.</li> <li><code class="highlighter-rouge">MissingAttribute</code> - self explanatory, when key is missing in json or when after parsing the value is nil and should not be.</li> </ul> <p>A <code class="highlighter-rouge">ResponseMapper</code> may look like this</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">class</span> <span class="kt">ResponseMapper</span><span class="o">&lt;</span><span class="kt">A</span><span class="p">:</span> <span class="kt">ParsedItem</span><span class="o">&gt;</span> <span class="p">{</span> <span class="kd">static</span> <span class="kd">func</span> <span class="nf">process</span><span class="p">(</span><span class="nv">obj</span><span class="p">:</span> <span class="kt">AnyObject</span><span class="p">?,</span> <span class="nv">parse</span><span class="p">:</span> <span class="p">(</span><span class="nv">json</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">AnyObject</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="kt">A</span><span class="p">?)</span> <span class="k">throws</span> <span class="o">-&gt;</span> <span class="kt">A</span> <span class="p">{</span> <span class="k">guard</span> <span class="k">let</span> <span class="nv">json</span> <span class="o">=</span> <span class="n">obj</span> <span class="k">as?</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">AnyObject</span><span class="p">]</span> <span class="k">else</span> <span class="p">{</span> <span class="k">throw</span> <span class="kt">ResponseMapperError</span><span class="o">.</span><span class="kt">Invalid</span> <span class="p">}</span> <span class="k">if</span> <span class="k">let</span> <span class="nv">item</span> <span class="o">=</span> <span class="nf">parse</span><span class="p">(</span><span class="nv">json</span><span class="p">:</span> <span class="n">json</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="n">item</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="kt">L</span><span class="o">.</span><span class="nf">log</span><span class="p">(</span><span class="s">"Mapper failure (</span><span class="se">\(</span><span class="k">self</span><span class="se">)</span><span class="s">). Missing attribute."</span><span class="p">)</span> <span class="k">throw</span> <span class="kt">ResponseMapperError</span><span class="o">.</span><span class="kt">MissingAttribute</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>It takes an <code class="highlighter-rouge">obj</code> which is response from the backend - a JSON in our case, and a <code class="highlighter-rouge">parse</code> method which consumes this <code class="highlighter-rouge">obj</code> and returns <code class="highlighter-rouge">A</code> object that conforms to <code class="highlighter-rouge">ParsedItem</code>.</p> <p>Now that we have this generic mapper we can create concrete mappers. Let’s take a look on a mapper used for parsing response of <em>Sign In</em> operation.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">protocol</span> <span class="kt">ResponseMapperProtocol</span> <span class="p">{</span> <span class="k">associatedtype</span> <span class="kt">Item</span> <span class="kd">static</span> <span class="kd">func</span> <span class="nf">process</span><span class="p">(</span><span class="nv">obj</span><span class="p">:</span> <span class="kt">AnyObject</span><span class="p">?)</span> <span class="k">throws</span> <span class="o">-&gt;</span> <span class="kt">Item</span> <span class="p">}</span> <span class="kd">final</span> <span class="kd">class</span> <span class="kt">SignInResponseMapper</span><span class="p">:</span> <span class="kt">ResponseMapper</span><span class="o">&lt;</span><span class="kt">SignInItem</span><span class="o">&gt;</span><span class="p">,</span> <span class="kt">ResponseMapperProtocol</span> <span class="p">{</span> <span class="kd">static</span> <span class="kd">func</span> <span class="nf">process</span><span class="p">(</span><span class="nv">obj</span><span class="p">:</span> <span class="kt">AnyObject</span><span class="p">?)</span> <span class="k">throws</span> <span class="o">-&gt;</span> <span class="kt">SignInItem</span> <span class="p">{</span> <span class="k">return</span> <span class="k">try</span> <span class="nf">process</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="nv">parse</span><span class="p">:</span> <span class="p">{</span> <span class="n">json</span> <span class="k">in</span> <span class="k">let</span> <span class="nv">token</span> <span class="o">=</span> <span class="n">json</span><span class="p">[</span><span class="s">"token"</span><span class="p">]</span> <span class="k">as?</span> <span class="kt">String</span> <span class="k">let</span> <span class="nv">uniqueId</span> <span class="o">=</span> <span class="n">json</span><span class="p">[</span><span class="s">"unique_id"</span><span class="p">]</span> <span class="k">as?</span> <span class="kt">String</span> <span class="k">if</span> <span class="k">let</span> <span class="nv">token</span> <span class="o">=</span> <span class="n">token</span><span class="p">,</span> <span class="k">let</span> <span class="nv">uniqueId</span> <span class="o">=</span> <span class="n">uniqueId</span> <span class="p">{</span> <span class="k">return</span> <span class="kt">SignInItem</span><span class="p">(</span><span class="nv">token</span><span class="p">:</span> <span class="n">token</span><span class="p">,</span> <span class="nv">uniqueId</span><span class="p">:</span> <span class="n">uniqueId</span><span class="p">)</span> <span class="p">}</span> <span class="k">return</span> <span class="kc">nil</span> <span class="p">})</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>The <code class="highlighter-rouge">ResponseMapperProtocol</code> is a protocol to be implemented by concrete mappers so they share the same method for parsing response.</p> <p>Then, such a mapper is used in a success block of an operation and you can operate on concrete object of specific type instead of dictionary. Easy to use such object later, evertything is easy to test.</p> <p>The last thing is a response mapper for parsing arrays.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">final</span> <span class="kd">class</span> <span class="kt">ArrayResponseMapper</span><span class="o">&lt;</span><span class="kt">A</span><span class="p">:</span> <span class="kt">ParsedItem</span><span class="o">&gt;</span> <span class="p">{</span> <span class="kd">static</span> <span class="kd">func</span> <span class="nf">process</span><span class="p">(</span><span class="nv">obj</span><span class="p">:</span> <span class="kt">AnyObject</span><span class="p">?,</span> <span class="nv">mapper</span><span class="p">:</span> <span class="p">(</span><span class="kt">AnyObject</span><span class="p">?</span> <span class="k">throws</span> <span class="o">-&gt;</span> <span class="kt">A</span><span class="p">))</span> <span class="k">throws</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="kt">A</span><span class="p">]</span> <span class="p">{</span> <span class="k">guard</span> <span class="k">let</span> <span class="nv">json</span> <span class="o">=</span> <span class="n">obj</span> <span class="k">as?</span> <span class="p">[[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">AnyObject</span><span class="p">]]</span> <span class="k">else</span> <span class="p">{</span> <span class="k">throw</span> <span class="kt">ResponseMapperError</span><span class="o">.</span><span class="kt">Invalid</span> <span class="p">}</span> <span class="k">var</span> <span class="nv">items</span> <span class="o">=</span> <span class="p">[</span><span class="kt">A</span><span class="p">]()</span> <span class="k">for</span> <span class="n">jsonNode</span> <span class="k">in</span> <span class="n">json</span> <span class="p">{</span> <span class="k">let</span> <span class="nv">item</span> <span class="o">=</span> <span class="k">try</span> <span class="nf">mapper</span><span class="p">(</span><span class="n">jsonNode</span><span class="p">)</span> <span class="n">items</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="n">item</span><span class="p">)</span> <span class="p">}</span> <span class="k">return</span> <span class="n">items</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>It takes a mapping function and return array of items if everything is correctly parsed. Depends on what you expect you might throw an error if just a single item can’t be parsed or return empty array in worst case as a product of this mapper. The mapper expects that the <code class="highlighter-rouge">obj</code> (response from the backend) is an array of JSON elements.</p> <p>Here is diagram that presents the network layer architecture.</p> <p><a href="/uploads/post-53/diagram.png"><img src="/uploads/post-53/diagram.png" alt="diagram" /></a></p> <h3 id="example-project">Example project</h3> <p>You can find an example project here <a href="https://github.com/tomkowz/NetworkLayerExample">on my github</a>. The project is using fake url for the backend, so no request will finish with success. I made this available only to give you a view of how the foundation of the network layer look like.</p> <h3 id="wrap-up">Wrap up</h3> <p>I found this way of doing network layer very useful, simple and easy to work with.</p> <ul> <li>The biggest advantage of it is that you can easily add new operations that will have similar design to others and that it does not know anything about Core Data.</li> <li>You can keep the code coverage close to 100% with no big effort, without thinking how to cover a super hard cases, because there is no such cases at all.</li> <li>The core of it is easy to reuse in other applications that have similar complexity.</li> </ul> Sat, 30 Jul 2016 00:00:00 +0200 http://szulctomasz.com/how-do-I-build-a-network-layer/ http://szulctomasz.com/how-do-I-build-a-network-layer/ ios, swift, network layer, request, response, design, diagram, parser, mapper, mapping iOS: A Beautiful Way of Styling IBOutlets in Swift <p>Today I was struggling with styling views. Usually I am trying to do what I am able to do in storyboard files. Next, I am creating <code class="highlighter-rouge">@IBOutlet</code> references and do the rest in the view controllers code - That’s not ideal, I know.</p> <p>Today’s problem was a bit more complicated. I am using a lot of placeholder views in storyboards that will hold more complicated custom controls. These type of controls that have only placeholder reference in storyboard is very difficult to fully customize in storyboards. I mean, you can use <code class="highlighter-rouge">@IBDesignable</code> but at some point you’ll be duplicating properties of the internal views. I am a big fan of exposing views contained in this control and customize them outside - but not about it this time.</p> <p>So, I was thinking how can I setup control without messing around with the view controller code and logic. Googling a bit took me to <a href="https://www.natashatherobot.com/ios-a-beautiful-way-of-styling-iboutlets-in-swift/">iOS: A Beautiful Way of Styling IBOutlets in Swift</a> by <a href="http://twitter.com/natashatherobot">@NatashaTheRobot</a> (Go there and read, I’ll wait).</p> <p>A code example taken from the original post:</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">import</span> <span class="kt">UIKit</span> <span class="kd">class</span> <span class="kt">ViewController</span><span class="p">:</span> <span class="kt">UIViewController</span> <span class="p">{</span> <span class="kd">@IBOutlet</span> <span class="k">weak</span> <span class="k">var</span> <span class="nv">myLabel</span><span class="p">:</span> <span class="kt">UILabel</span><span class="o">!</span> <span class="p">{</span> <span class="k">didSet</span> <span class="p">{</span> <span class="n">myLabel</span><span class="o">.</span><span class="n">textColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="nf">purpleColor</span><span class="p">()</span> <span class="p">}</span> <span class="p">}</span> <span class="kd">@IBOutlet</span> <span class="k">weak</span> <span class="k">var</span> <span class="nv">myOtherLabel</span><span class="p">:</span> <span class="kt">UILabel</span><span class="o">!</span> <span class="p">{</span> <span class="k">didSet</span> <span class="p">{</span> <span class="n">myOtherLabel</span><span class="o">.</span><span class="n">textColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="nf">yellowColor</span><span class="p">()</span> <span class="p">}</span> <span class="p">}</span> <span class="kd">@IBOutlet</span> <span class="k">weak</span> <span class="k">var</span> <span class="nv">myButton</span><span class="p">:</span> <span class="kt">UIButton</span><span class="o">!</span> <span class="p">{</span> <span class="k">didSet</span> <span class="p">{</span> <span class="n">myButton</span><span class="o">.</span><span class="n">tintColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="nf">magentaColor</span><span class="p">()</span> <span class="p">}</span> <span class="p">}</span> <span class="k">override</span> <span class="kd">func</span> <span class="nf">viewDidLoad</span><span class="p">()</span> <span class="p">{</span> <span class="k">super</span><span class="o">.</span><span class="nf">viewDidLoad</span><span class="p">()</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>The idea she described is kinda beautiful, and I adopted it. Then I realized I had to do a lot of customization with my controls. I immediately stopped liking the solution and started to hating it because I noticed the view controller is getting bigger and bigger.</p> <p>Styling outlets this way cost me <em>2 + n</em> lines of code, when <em>n</em> is number of lines of styling code. You can also add this empty line between outlets just for readability. Having 10 properties for styling is a lot of additional code and lines we can probably somehow avoid.</p> <p><em>I am not saying this way is poor and you have to stop doing this way. That’d not be true. I actually like it, but it is not the best way to go in case of my current project.</em></p> <h3 id="another-beautiful-way">Another beautiful(?) way</h3> <p>Let’s start with a bit simplified example. I’ll be styling labels, a button and a view, but you can think about more real-life example that I mentioned above - every control is a custom one that you have to style somewhere - you can also call some DRY method you created to avoid repeating styling-code - I hope you wrote such method :)</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">class</span> <span class="kt">ManyLabelsViewController</span><span class="p">:</span> <span class="kt">UIViewController</span> <span class="p">{</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">label1</span><span class="p">:</span> <span class="kt">UILabel</span><span class="o">!</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">label2</span><span class="p">:</span> <span class="kt">UILabel</span><span class="o">!</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">label3</span><span class="p">:</span> <span class="kt">UILabel</span><span class="o">!</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">label4</span><span class="p">:</span> <span class="kt">UILabel</span><span class="o">!</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">label5</span><span class="p">:</span> <span class="kt">UILabel</span><span class="o">!</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">label6</span><span class="p">:</span> <span class="kt">UILabel</span><span class="o">!</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">button1</span><span class="p">:</span> <span class="kt">UIButton</span><span class="o">!</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">view1</span><span class="p">:</span> <span class="kt">UIView</span><span class="o">!</span> <span class="k">override</span> <span class="kd">func</span> <span class="nf">viewDidLoad</span><span class="p">()</span> <span class="p">{</span> <span class="k">super</span><span class="o">.</span><span class="nf">viewDidLoad</span><span class="p">()</span> <span class="c1">// something goes here...</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>Let’s add some styling.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">import</span> <span class="kt">UIKit</span> <span class="kd">class</span> <span class="kt">ManyLabelsViewController</span><span class="p">:</span> <span class="kt">UIViewController</span> <span class="p">{</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">label1</span><span class="p">:</span> <span class="kt">UILabel</span><span class="o">!</span> <span class="p">{</span> <span class="k">didSet</span> <span class="p">{</span> <span class="n">label1</span><span class="o">.</span><span class="n">textColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="nf">redColor</span><span class="p">()</span> <span class="n">label1</span><span class="o">.</span><span class="n">font</span> <span class="o">=</span> <span class="kt">UIFont</span><span class="o">.</span><span class="nf">systemFontOfSize</span><span class="p">(</span><span class="mi">20</span><span class="p">)</span> <span class="n">label1</span><span class="o">.</span><span class="n">backgroundColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="nf">blueColor</span><span class="p">()</span> <span class="p">}</span> <span class="p">}</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">label2</span><span class="p">:</span> <span class="kt">UILabel</span><span class="o">!</span> <span class="p">{</span> <span class="k">didSet</span> <span class="p">{</span> <span class="n">label2</span><span class="o">.</span><span class="n">layer</span><span class="o">.</span><span class="n">borderColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="nf">yellowColor</span><span class="p">()</span><span class="o">.</span><span class="kt">CGColor</span> <span class="n">label2</span><span class="o">.</span><span class="n">layer</span><span class="o">.</span><span class="n">borderWidth</span> <span class="o">=</span> <span class="mi">1</span> <span class="n">label2</span><span class="o">.</span><span class="n">backgroundColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="nf">blueColor</span><span class="p">()</span> <span class="n">label2</span><span class="o">.</span><span class="n">clipsToBounds</span> <span class="o">=</span> <span class="kc">true</span> <span class="p">}</span> <span class="p">}</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">label3</span><span class="p">:</span> <span class="kt">UILabel</span><span class="o">!</span> <span class="p">{</span> <span class="k">didSet</span> <span class="p">{</span> <span class="n">label3</span><span class="o">.</span><span class="n">textColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="nf">purpleColor</span><span class="p">()</span> <span class="p">}</span> <span class="p">}</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">label4</span><span class="p">:</span> <span class="kt">UILabel</span><span class="o">!</span> <span class="p">{</span> <span class="k">didSet</span> <span class="p">{</span> <span class="n">label4</span><span class="o">.</span><span class="n">textAlignment</span> <span class="o">=</span> <span class="o">.</span><span class="kt">Center</span> <span class="n">label4</span><span class="o">.</span><span class="n">textColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="nf">grayColor</span><span class="p">()</span> <span class="p">}</span> <span class="p">}</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">label5</span><span class="p">:</span> <span class="kt">UILabel</span><span class="o">!</span> <span class="p">{</span> <span class="k">didSet</span> <span class="p">{</span> <span class="n">label5</span><span class="o">.</span><span class="n">backgroundColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="nf">greenColor</span><span class="p">()</span> <span class="n">label5</span><span class="o">.</span><span class="n">font</span> <span class="o">=</span> <span class="kt">UIFont</span><span class="o">.</span><span class="nf">boldSystemFontOfSize</span><span class="p">(</span><span class="mi">28</span><span class="p">)</span> <span class="p">}</span> <span class="p">}</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">label6</span><span class="p">:</span> <span class="kt">UILabel</span><span class="o">!</span> <span class="p">{</span> <span class="k">didSet</span> <span class="p">{</span> <span class="n">label6</span><span class="o">.</span><span class="n">lineBreakMode</span> <span class="o">=</span> <span class="o">.</span><span class="kt">ByClipping</span> <span class="n">label6</span><span class="o">.</span><span class="n">numberOfLines</span> <span class="o">=</span> <span class="mi">0</span> <span class="p">}</span> <span class="p">}</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">button1</span><span class="p">:</span> <span class="kt">UIButton</span><span class="o">!</span> <span class="p">{</span> <span class="k">didSet</span> <span class="p">{</span> <span class="n">button1</span><span class="o">.</span><span class="n">layer</span><span class="o">.</span><span class="n">borderWidth</span> <span class="o">=</span> <span class="mi">1</span> <span class="n">button1</span><span class="o">.</span><span class="n">layer</span><span class="o">.</span><span class="n">borderColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="nf">blueColor</span><span class="p">()</span><span class="o">.</span><span class="kt">CGColor</span> <span class="p">}</span> <span class="p">}</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">view1</span><span class="p">:</span> <span class="kt">UIView</span><span class="o">!</span> <span class="p">{</span> <span class="k">didSet</span> <span class="p">{</span> <span class="n">view1</span><span class="o">.</span><span class="n">backgroundColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="nf">cyanColor</span><span class="p">()</span> <span class="p">}</span> <span class="p">}</span> <span class="k">override</span> <span class="kd">func</span> <span class="nf">viewDidLoad</span><span class="p">()</span> <span class="p">{</span> <span class="k">super</span><span class="o">.</span><span class="nf">viewDidLoad</span><span class="p">()</span> <span class="c1">// something goes here...</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>Wow… This turned out to be 65 lines of code in the view controller and there is no logic inside yet. It was 17 before we started. This does not sound like beautiful styling at all. Can you see properties declarations? For sure yes, that’s clear and nicely visible.</p> <p>It gets even worse when you have to create some outlets just for styling. If there would be no custom styling you would not be creating such outlet.</p> <p><em>Can we do better than this? Yes. Is this an universal solution? Not sure, it works in my case, and I get better results than the method described above.</em></p> <p>As we’re using storyboards anyway, why don’t we create some styling object that would help as doing dirty work? We could create it by dragging <strong>Object</strong> object on our view controller in storyboard, create class suffixed with <code class="highlighter-rouge">*ViewControllerStyle</code> connect outlets there and do the styling. You can create reference to a specific view/control in many objects, not just view controller.</p> <p><img src="/uploads/post-52/img1.png" alt="img1" /></p> <p>Doing it this way, the code has been detached into <code class="highlighter-rouge">ManyLabelsViewControllerStyle</code> class.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">class</span> <span class="kt">ManyLabelsViewControllerStyle</span><span class="p">:</span> <span class="kt">NSObject</span> <span class="p">{</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">weak</span> <span class="k">var</span> <span class="nv">label1</span><span class="p">:</span> <span class="kt">UILabel</span><span class="o">!</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">weak</span> <span class="k">var</span> <span class="nv">label2</span><span class="p">:</span> <span class="kt">UILabel</span><span class="o">!</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">weak</span> <span class="k">var</span> <span class="nv">label3</span><span class="p">:</span> <span class="kt">UILabel</span><span class="o">!</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">weak</span> <span class="k">var</span> <span class="nv">label4</span><span class="p">:</span> <span class="kt">UILabel</span><span class="o">!</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">weak</span> <span class="k">var</span> <span class="nv">label5</span><span class="p">:</span> <span class="kt">UILabel</span><span class="o">!</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">weak</span> <span class="k">var</span> <span class="nv">label6</span><span class="p">:</span> <span class="kt">UILabel</span><span class="o">!</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">weak</span> <span class="k">var</span> <span class="nv">button1</span><span class="p">:</span> <span class="kt">UIButton</span><span class="o">!</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">weak</span> <span class="k">var</span> <span class="nv">view1</span><span class="p">:</span> <span class="kt">UIView</span><span class="o">!</span> <span class="kd">func</span> <span class="nf">style</span><span class="p">()</span> <span class="p">{</span> <span class="nf">styleLabel1</span><span class="p">()</span> <span class="nf">styleLabel2</span><span class="p">()</span> <span class="nf">styleLabel3</span><span class="p">()</span> <span class="nf">styleLabel4</span><span class="p">()</span> <span class="nf">styleLabel5</span><span class="p">()</span> <span class="nf">styleLabel6</span><span class="p">()</span> <span class="nf">styleButton1</span><span class="p">()</span> <span class="nf">styleView1</span><span class="p">()</span> <span class="p">}</span> <span class="kd">private</span> <span class="kd">func</span> <span class="nf">styleLabel1</span><span class="p">()</span> <span class="p">{</span> <span class="n">label1</span><span class="o">.</span><span class="n">textColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="nf">redColor</span><span class="p">()</span> <span class="n">label1</span><span class="o">.</span><span class="n">font</span> <span class="o">=</span> <span class="kt">UIFont</span><span class="o">.</span><span class="nf">systemFontOfSize</span><span class="p">(</span><span class="mi">20</span><span class="p">)</span> <span class="n">label1</span><span class="o">.</span><span class="n">backgroundColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="nf">blueColor</span><span class="p">()</span> <span class="p">}</span> <span class="kd">private</span> <span class="kd">func</span> <span class="nf">styleLabel2</span><span class="p">()</span> <span class="p">{</span> <span class="n">label2</span><span class="o">.</span><span class="n">layer</span><span class="o">.</span><span class="n">borderColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="nf">yellowColor</span><span class="p">()</span><span class="o">.</span><span class="kt">CGColor</span> <span class="n">label2</span><span class="o">.</span><span class="n">layer</span><span class="o">.</span><span class="n">borderWidth</span> <span class="o">=</span> <span class="mi">1</span> <span class="n">label2</span><span class="o">.</span><span class="n">backgroundColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="nf">blueColor</span><span class="p">()</span> <span class="n">label2</span><span class="o">.</span><span class="n">clipsToBounds</span> <span class="o">=</span> <span class="kc">true</span> <span class="p">}</span> <span class="kd">private</span> <span class="kd">func</span> <span class="nf">styleLabel3</span><span class="p">()</span> <span class="p">{</span> <span class="n">label3</span><span class="o">.</span><span class="n">textColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="nf">purpleColor</span><span class="p">()</span> <span class="p">}</span> <span class="kd">private</span> <span class="kd">func</span> <span class="nf">styleLabel4</span><span class="p">()</span> <span class="p">{</span> <span class="n">label4</span><span class="o">.</span><span class="n">textAlignment</span> <span class="o">=</span> <span class="o">.</span><span class="kt">Center</span> <span class="n">label4</span><span class="o">.</span><span class="n">textColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="nf">grayColor</span><span class="p">()</span> <span class="p">}</span> <span class="kd">private</span> <span class="kd">func</span> <span class="nf">styleLabel5</span><span class="p">()</span> <span class="p">{</span> <span class="n">label5</span><span class="o">.</span><span class="n">backgroundColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="nf">greenColor</span><span class="p">()</span> <span class="n">label5</span><span class="o">.</span><span class="n">font</span> <span class="o">=</span> <span class="kt">UIFont</span><span class="o">.</span><span class="nf">boldSystemFontOfSize</span><span class="p">(</span><span class="mi">28</span><span class="p">)</span> <span class="p">}</span> <span class="kd">private</span> <span class="kd">func</span> <span class="nf">styleLabel6</span><span class="p">()</span> <span class="p">{</span> <span class="n">label6</span><span class="o">.</span><span class="n">lineBreakMode</span> <span class="o">=</span> <span class="o">.</span><span class="kt">ByClipping</span> <span class="n">label6</span><span class="o">.</span><span class="n">numberOfLines</span> <span class="o">=</span> <span class="mi">0</span> <span class="p">}</span> <span class="kd">private</span> <span class="kd">func</span> <span class="nf">styleButton1</span><span class="p">()</span> <span class="p">{</span> <span class="n">button1</span><span class="o">.</span><span class="n">layer</span><span class="o">.</span><span class="n">borderWidth</span> <span class="o">=</span> <span class="mi">1</span> <span class="n">button1</span><span class="o">.</span><span class="n">layer</span><span class="o">.</span><span class="n">borderColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="nf">blueColor</span><span class="p">()</span><span class="o">.</span><span class="kt">CGColor</span> <span class="p">}</span> <span class="kd">private</span> <span class="kd">func</span> <span class="nf">styleView1</span><span class="p">()</span> <span class="p">{</span> <span class="n">view1</span><span class="o">.</span><span class="n">backgroundColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="nf">cyanColor</span><span class="p">()</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>As we don’t need references to the controls in view controller we can remove all of them. Or just keep these you need.</p> <p>You need to call the <code class="highlighter-rouge">style()</code> method at some point, so the last thing is to create outlet to such object in the view controller and call <code class="highlighter-rouge">style()</code> in the <code class="highlighter-rouge">viewDidLoad</code>.</p> <p>Here is how the view controller look like at the end.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">class</span> <span class="kt">ManyLabelsViewController</span><span class="p">:</span> <span class="kt">UIViewController</span> <span class="p">{</span> <span class="c1">// Controls you need to keep for calling them...</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">style</span><span class="p">:</span> <span class="kt">ManyLabelsViewControllerStyle</span><span class="o">!</span> <span class="k">override</span> <span class="kd">func</span> <span class="nf">viewDidLoad</span><span class="p">()</span> <span class="p">{</span> <span class="k">super</span><span class="o">.</span><span class="nf">viewDidLoad</span><span class="p">()</span> <span class="n">style</span><span class="o">.</span><span class="nf">style</span><span class="p">()</span> <span class="c1">// something goes here...</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <hr /> <p><strong>Updated 30 Jun 2016</strong></p> <p>You can also use just <em>didSet</em> in this <em>styling</em> object and just let it configure the outlets when they are set.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">class</span> <span class="kt">ManyLabelsViewControllerStyle</span><span class="p">:</span> <span class="kt">NSObject</span> <span class="p">{</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">weak</span> <span class="k">var</span> <span class="nv">label1</span><span class="p">:</span> <span class="kt">UILabel</span><span class="o">!</span> <span class="p">{</span> <span class="k">didSet</span> <span class="p">{</span> <span class="n">label1</span><span class="o">.</span><span class="n">textColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="nf">redColor</span><span class="p">()</span> <span class="n">label1</span><span class="o">.</span><span class="n">font</span> <span class="o">=</span> <span class="kt">UIFont</span><span class="o">.</span><span class="nf">systemFontOfSize</span><span class="p">(</span><span class="mi">20</span><span class="p">)</span> <span class="n">label1</span><span class="o">.</span><span class="n">backgroundColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="nf">blueColor</span><span class="p">()</span> <span class="p">}</span> <span class="p">}</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">weak</span> <span class="k">var</span> <span class="nv">label2</span><span class="p">:</span> <span class="kt">UILabel</span><span class="o">!</span> <span class="p">{</span> <span class="k">didSet</span> <span class="p">{</span> <span class="n">label2</span><span class="o">.</span><span class="n">layer</span><span class="o">.</span><span class="n">borderColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="nf">yellowColor</span><span class="p">()</span><span class="o">.</span><span class="kt">CGColor</span> <span class="n">label2</span><span class="o">.</span><span class="n">layer</span><span class="o">.</span><span class="n">borderWidth</span> <span class="o">=</span> <span class="mi">1</span> <span class="n">label2</span><span class="o">.</span><span class="n">backgroundColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="nf">blueColor</span><span class="p">()</span> <span class="n">label2</span><span class="o">.</span><span class="n">clipsToBounds</span> <span class="o">=</span> <span class="kc">true</span> <span class="p">}</span> <span class="p">}</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">weak</span> <span class="k">var</span> <span class="nv">label3</span><span class="p">:</span> <span class="kt">UILabel</span><span class="o">!</span> <span class="p">{</span> <span class="k">didSet</span> <span class="p">{</span> <span class="n">label3</span><span class="o">.</span><span class="n">textColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="nf">purpleColor</span><span class="p">()</span> <span class="p">}</span> <span class="p">}</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">weak</span> <span class="k">var</span> <span class="nv">label4</span><span class="p">:</span> <span class="kt">UILabel</span><span class="o">!</span> <span class="p">{</span> <span class="k">didSet</span> <span class="p">{</span> <span class="n">label4</span><span class="o">.</span><span class="n">textAlignment</span> <span class="o">=</span> <span class="o">.</span><span class="kt">Center</span> <span class="n">label4</span><span class="o">.</span><span class="n">textColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="nf">grayColor</span><span class="p">()</span> <span class="p">}</span> <span class="p">}</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">weak</span> <span class="k">var</span> <span class="nv">label5</span><span class="p">:</span> <span class="kt">UILabel</span><span class="o">!</span> <span class="p">{</span> <span class="k">didSet</span> <span class="p">{</span> <span class="n">label5</span><span class="o">.</span><span class="n">backgroundColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="nf">greenColor</span><span class="p">()</span> <span class="n">label5</span><span class="o">.</span><span class="n">font</span> <span class="o">=</span> <span class="kt">UIFont</span><span class="o">.</span><span class="nf">boldSystemFontOfSize</span><span class="p">(</span><span class="mi">28</span><span class="p">)</span> <span class="p">}</span> <span class="p">}</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">weak</span> <span class="k">var</span> <span class="nv">label6</span><span class="p">:</span> <span class="kt">UILabel</span><span class="o">!</span> <span class="p">{</span> <span class="k">didSet</span> <span class="p">{</span> <span class="n">label6</span><span class="o">.</span><span class="n">lineBreakMode</span> <span class="o">=</span> <span class="o">.</span><span class="kt">ByClipping</span> <span class="n">label6</span><span class="o">.</span><span class="n">numberOfLines</span> <span class="o">=</span> <span class="mi">0</span> <span class="p">}</span> <span class="p">}</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">weak</span> <span class="k">var</span> <span class="nv">button1</span><span class="p">:</span> <span class="kt">UIButton</span><span class="o">!</span> <span class="p">{</span> <span class="k">didSet</span> <span class="p">{</span> <span class="n">button1</span><span class="o">.</span><span class="n">layer</span><span class="o">.</span><span class="n">borderWidth</span> <span class="o">=</span> <span class="mi">1</span> <span class="n">button1</span><span class="o">.</span><span class="n">layer</span><span class="o">.</span><span class="n">borderColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="nf">blueColor</span><span class="p">()</span><span class="o">.</span><span class="kt">CGColor</span> <span class="p">}</span> <span class="p">}</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">weak</span> <span class="k">var</span> <span class="nv">view1</span><span class="p">:</span> <span class="kt">UIView</span><span class="o">!</span> <span class="p">{</span> <span class="k">didSet</span> <span class="p">{</span> <span class="n">view1</span><span class="o">.</span><span class="n">backgroundColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="nf">cyanColor</span><span class="p">()</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>And then your view controller look like this:</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">class</span> <span class="kt">ManyLabelsViewController</span><span class="p">:</span> <span class="kt">UIViewController</span> <span class="p">{</span> <span class="c1">// Controls you need to keep for calling them...</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">style</span><span class="p">:</span> <span class="kt">ManyLabelsViewControllerStyle</span><span class="o">!</span> <span class="c1">// no extra call needed.</span> <span class="p">}</span></code></pre></figure> Wed, 22 Jun 2016 00:00:00 +0200 http://szulctomasz.com/ios-a-beautiful-way-of-styling-iboutlets-in-swift/ http://szulctomasz.com/ios-a-beautiful-way-of-styling-iboutlets-in-swift/ ios, swift, iboutlets, storyboards Finding contours on paper sheet scans <p><em>This article covers what data do we need to collect to learn neural network to recognize handwritten digits and how to collect that data using OS X app written in Swift. This can be however easily ported to iOS, so if you’re not familiar with OS X you can find many useful informations anyway.</em></p> <hr /> <p>Severals months back I decided to get deeper understanding of what machine learning is, how to create neural networks and how to recognize handwritten digits.</p> <p>The Machine Learning (ML) domain is broad topic and is getting more and more popular each year. Big companies that has some photos hosting or photos sharing services uses neural networks to recognize people faces to create albums where you can find photos of just one person. Some services uses machine learning to learn when to show you an ad on their page, to make sure you’ll be willingly clicking it or at least reading. Siri uses it too. There is a lot of use cases nowadays where neural networks exist.</p> <p>Today, I am several weeks after I finished <a href="https://www.coursera.org/learn/machine-learning">Machine Learning by Stanford University</a> course. This helped me understand basic stuff of ML and neural networks.</p> <p>Before I start working on such neural network, I wanted to fetch data of digits written by me first and use it for feeding neural network (NN) and test it later also using my digits.</p> <p>To learn NN how your digits look like you need to feed it with a lot of examples of a single digit.</p> <h3 id="collecting-data">Collecting data</h3> <p>I started of collecting 100 examples of each digit (0-9). The next step is to get this data out of the paper sheet scans, cut the digits out of the scans and store in files that share the same dimensions. Later on these small images representing one digit will be converted to more useful data for the NN. The image will be represented by a MxM matrix that contains information about every pixel of these images with one digit. The individual item in a matrix is an argument of a function that NN will use to learn. So, having 20x20 pixels images will end up of having function that will take 400 parameters. The bigger the picture is, the more details it has, the longer time will it take to learn.</p> <p>Getting 100 examples of each digits is a lot of writing.. As a software dev I am not typing too often using a pen, so, it is quite time consuming and my hand is hurting now :) However, to get more examples you can take the scanned digits and e.g. rotate them to create new examples.</p> <p><img src="/uploads/post-51/picture-1.png" alt="preparing-data" /></p> <h3 id="contours-detection---idea">Contours detection - idea</h3> <p>To do a simple contours recognition and prepare images for later use we need to think about 3 things - <strong>size of a slice</strong> (group of pixels) of a scan you’ll be examinating to have or not to have an interesting information, <strong>threshold</strong> that will decide whether examinated data is the interesting one or not (everything above threshold is not interesting), and <strong>size of an output images</strong> which is important for your future neural network.</p> <p>Okay, we’ve got all this parameters but what next? Now, algorithm need to loop over all pixels divided by slices of width (square is fine for this) you chose, and if there is at least one interesting pixel in this slice the algorithm stops examinating this slice and start checking neighborhood for other interesting slices. After it found first interesting slice it has to group all neighbor slices because it will be a contour we’re looking for. Following sketch presents roughly what’s going on.</p> <p><img src="/uploads/post-51/detecting-1.png" alt="detecting-1" /></p> <p>Slices with solid line borders are ones with no contours inside. At some point the algorithm will find a slice with dotted borders <strong>(1)</strong>. Then it creates a group and start examinating neighborhood for another dotted-border slices and group them together <strong>(2)</strong>. After there is no interesting slices to collect, it stops searching for this specific contour and stores it for later. Every time slice is checked the algorithm marks it as visited to not visit it again.</p> <p>Having group of slices you can calculate an area that will cover all of them. This area is the area of the contour on the image. The area is used to cut the contour from the original image and later saved on disk.</p> <p>Having such group of slices you can also draw preview of what algorithm found and whether it correctly calculated area and whether it missed some slices or not. This stuff turned out to be really useful during development.</p> <h3 id="app">App</h3> <p>I decided to go with OS X app this time. I did it because of the Mac performance. I wanted to detect contours quickly and have them saved on my Mac’s disk.</p> <p>All this led me to following UI <img src="/uploads/post-51/app-screenshot.png" alt="app-screnshot" /></p> <p>I’ll not cover code of the view controller or UI logic in this post. Only snippets of algorithm will be covered.</p> <h3 id="loading-image">Loading image</h3> <p>What we need to do first is to provide path to the image file on disk and load it as CGImage for later processing.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">let</span> <span class="nv">fileSystemRep</span> <span class="o">=</span> <span class="p">(</span><span class="n">path</span> <span class="k">as</span> <span class="kt">NSString</span><span class="p">)</span><span class="o">.</span><span class="n">fileSystemRepresentation</span> <span class="k">if</span> <span class="k">let</span> <span class="nv">dataProvider</span> <span class="o">=</span> <span class="kt">CGDataProviderCreateWithFilename</span><span class="p">(</span><span class="n">fileSystemRep</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="k">let</span> <span class="nv">imageSource</span> <span class="o">=</span> <span class="kt">CGImageSourceCreateWithDataProvider</span><span class="p">(</span><span class="n">dataProvider</span><span class="p">,</span> <span class="kc">nil</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="k">let</span> <span class="nv">img</span> <span class="o">=</span> <span class="kt">CGImageSourceCreateImageAtIndex</span><span class="p">(</span><span class="n">imageSource</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="kc">nil</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="n">img</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <h3 id="getting-pixel-data">Getting pixel data</h3> <p>After image is loaded we have to obtain pixel data of the image that we can search through later. This can be easily achieved using Core Graphics functions.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">let</span> <span class="nv">provider</span> <span class="o">=</span> <span class="kt">CGImageGetDataProvider</span><span class="p">(</span><span class="n">cgImage</span><span class="p">)</span><span class="o">!</span> <span class="k">let</span> <span class="nv">data</span> <span class="o">=</span> <span class="kt">CGDataProviderCopyData</span><span class="p">(</span><span class="n">provider</span><span class="p">)</span> <span class="k">self</span><span class="o">.</span><span class="n">dataPtr</span> <span class="o">=</span> <span class="kt">CFDataGetBytePtr</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="k">self</span><span class="o">.</span><span class="n">length</span> <span class="o">=</span> <span class="kt">CFDataGetLength</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="k">let</span> <span class="nv">bytesPerRow</span> <span class="o">=</span> <span class="kt">CGImageGetBytesPerRow</span><span class="p">(</span><span class="n">cgImage</span><span class="p">)</span> <span class="k">self</span><span class="o">.</span><span class="n">bytesPerComponent</span> <span class="o">=</span> <span class="n">bytesPerRow</span> <span class="o">/</span> <span class="kt">CGImageGetWidth</span><span class="p">(</span><span class="n">cgImage</span><span class="p">)</span></code></pre></figure> <p>Now, <strong>dataPtr</strong> is an 1 dimensional array with pixel data that you can access by index as normal array. You have to know width and height of an image to calculate the position of the pixel in array.</p> <h3 id="processing-pixel-data">Processing pixel data</h3> <p><em>The easiest type of image to process and get contours from is the black-and-white image. If you have color one you should consider converting it to b&amp;w first, or your algorithm will have to do that.</em></p> <p>The easiest way to find an interesting slice is to check whether it has dark pixel. So, as we have RGBA pixel data now, we need to get light of all the pixels and use such data with light information only. All we need to do is to convert RGB pixel to HSL and take only L.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">for</span> <span class="n">i</span> <span class="k">in</span> <span class="mi">0</span><span class="o">.</span><span class="nf">stride</span><span class="p">(</span><span class="nv">to</span><span class="p">:</span> <span class="n">totalBytes</span><span class="p">,</span> <span class="nv">by</span><span class="p">:</span> <span class="mi">4</span><span class="p">)</span> <span class="p">{</span> <span class="k">let</span> <span class="nv">r</span> <span class="o">=</span> <span class="kt">Int</span><span class="p">(</span><span class="n">originalData</span><span class="o">.</span><span class="nf">byte</span><span class="p">(</span><span class="n">i</span> <span class="o">+</span> <span class="mi">0</span><span class="p">))</span> <span class="k">let</span> <span class="nv">g</span> <span class="o">=</span> <span class="kt">Int</span><span class="p">(</span><span class="n">originalData</span><span class="o">.</span><span class="nf">byte</span><span class="p">(</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">))</span> <span class="k">let</span> <span class="nv">b</span> <span class="o">=</span> <span class="kt">Int</span><span class="p">(</span><span class="n">originalData</span><span class="o">.</span><span class="nf">byte</span><span class="p">(</span><span class="n">i</span> <span class="o">+</span> <span class="mi">2</span><span class="p">))</span> <span class="k">let</span> <span class="nv">maxVal</span> <span class="o">=</span> <span class="nf">max</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="nf">max</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="n">b</span><span class="p">))</span> <span class="k">let</span> <span class="nv">minVal</span> <span class="o">=</span> <span class="nf">min</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="nf">min</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="n">b</span><span class="p">))</span> <span class="k">let</span> <span class="nv">lightInt</span><span class="p">:</span> <span class="kt">Int</span> <span class="o">=</span> <span class="p">(</span><span class="n">maxVal</span> <span class="o">+</span> <span class="n">minVal</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span> <span class="k">var</span> <span class="nv">light</span> <span class="o">=</span> <span class="kt">UInt8</span><span class="p">(</span><span class="nf">max</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nf">min</span><span class="p">(</span><span class="n">lightInt</span><span class="p">,</span> <span class="mi">255</span><span class="p">)))</span> <span class="n">light</span> <span class="o">=</span> <span class="n">light</span> <span class="o">&lt;</span> <span class="n">threshold</span> <span class="p">?</span> <span class="mi">0</span> <span class="p">:</span> <span class="mi">1</span> <span class="kt">CFDataAppendBytes</span><span class="p">(</span><span class="n">newData</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">light</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="p">}</span></code></pre></figure> <p>Here the algorithm uses <strong>threshold</strong> parameter we discussed above. If light value of a pixel is below the threshold you can set 0, otherwise set 1 (or whatever you want).</p> <p>We can now feed our contour finder with this processed data.</p> <h3 id="finding-contour">Finding contour</h3> <p>We know image size, we have light pixels data, so we can search through by creating slices of an image and examine groups of pixels. If there is pixel data with 0 light value, it means this slice has a contour and we should group it.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">func</span> <span class="nf">isContourAt</span><span class="p">(</span><span class="n">x</span> <span class="nv">x</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">y</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Bool</span> <span class="p">{</span> <span class="k">return</span> <span class="n">data</span><span class="o">.</span><span class="nf">byte</span><span class="p">((</span><span class="n">y</span> <span class="o">*</span> <span class="kt">Int</span><span class="p">(</span><span class="n">imageSize</span><span class="o">.</span><span class="n">width</span><span class="p">))</span> <span class="o">+</span> <span class="n">x</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span> <span class="p">}</span></code></pre></figure> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">for</span> <span class="n">col</span> <span class="k">in</span> <span class="mi">0</span><span class="o">.</span><span class="nf">stride</span><span class="p">(</span><span class="nv">to</span><span class="p">:</span> <span class="kt">Int</span><span class="p">(</span><span class="n">imageSize</span><span class="o">.</span><span class="n">width</span> <span class="o">-</span> <span class="n">sliceSize</span><span class="o">.</span><span class="n">width</span><span class="p">),</span> <span class="nv">by</span><span class="p">:</span> <span class="kt">Int</span><span class="p">(</span><span class="n">sliceSize</span><span class="o">.</span><span class="n">width</span><span class="p">))</span> <span class="p">{</span> <span class="k">for</span> <span class="n">row</span> <span class="k">in</span> <span class="mi">0</span><span class="o">.</span><span class="nf">stride</span><span class="p">(</span><span class="nv">to</span><span class="p">:</span> <span class="kt">Int</span><span class="p">(</span><span class="n">imageSize</span><span class="o">.</span><span class="n">height</span> <span class="o">-</span> <span class="n">sliceSize</span><span class="o">.</span><span class="n">height</span><span class="p">),</span> <span class="nv">by</span><span class="p">:</span> <span class="kt">Int</span><span class="p">(</span><span class="n">sliceSize</span><span class="o">.</span><span class="n">height</span><span class="p">))</span> <span class="p">{</span> <span class="c1">// create slice for examinated position</span> <span class="k">let</span> <span class="nv">rect</span> <span class="o">=</span> <span class="kt">CGRect</span><span class="p">(</span><span class="nv">x</span><span class="p">:</span> <span class="n">col</span><span class="p">,</span> <span class="nv">y</span><span class="p">:</span> <span class="n">row</span><span class="p">,</span> <span class="nv">width</span><span class="p">:</span> <span class="kt">Int</span><span class="p">(</span><span class="n">sliceSize</span><span class="o">.</span><span class="n">width</span><span class="p">),</span> <span class="nv">height</span><span class="p">:</span> <span class="kt">Int</span><span class="p">(</span><span class="n">sliceSize</span><span class="o">.</span><span class="n">height</span><span class="p">))</span> <span class="k">let</span> <span class="nv">startingSlice</span> <span class="o">=</span> <span class="kt">Slice</span><span class="p">(</span><span class="nv">rect</span><span class="p">:</span> <span class="n">rect</span><span class="p">,</span> <span class="nv">delegate</span><span class="p">:</span> <span class="k">self</span><span class="p">)</span> <span class="c1">// create temporary contour and try to reveal it</span> <span class="k">var</span> <span class="nv">sliceGroup</span> <span class="o">=</span> <span class="kt">SliceGroup</span><span class="p">()</span> <span class="nf">searchNeighborhood</span><span class="p">(</span><span class="o">&amp;</span><span class="n">sliceGroup</span><span class="p">,</span> <span class="nv">slice</span><span class="p">:</span> <span class="n">startingSlice</span><span class="p">)</span> <span class="c1">// if examinated area contains contours you can store it</span> <span class="k">if</span> <span class="n">sliceGroup</span><span class="o">.</span><span class="n">slices</span><span class="o">.</span><span class="n">count</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="p">{</span> <span class="k">let</span> <span class="nv">contour</span> <span class="o">=</span> <span class="kt">Contour</span><span class="p">(</span><span class="nv">rect</span><span class="p">:</span> <span class="n">sliceGroup</span><span class="o">.</span><span class="n">rect</span><span class="p">,</span> <span class="nv">sliceRects</span><span class="p">:</span> <span class="n">sliceGroup</span><span class="o">.</span><span class="n">sliceRects</span><span class="p">)</span> <span class="n">delegate</span><span class="p">?</span><span class="o">.</span><span class="nf">finderDidFind</span><span class="p">(</span><span class="n">contour</span><span class="p">,</span> <span class="nv">originalImageSize</span><span class="p">:</span> <span class="n">imageSize</span><span class="p">)</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>Having one such slice we start searching recursively until we do not find any slices with contour inside.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="c1">// Do not proceed with checked slice</span> <span class="k">if</span> <span class="n">visited</span><span class="o">.</span><span class="nf">isVisited</span><span class="p">(</span><span class="n">sl</span><span class="o">.</span><span class="n">rect</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span> <span class="c1">// Mark this slice as checked</span> <span class="n">visited</span><span class="o">.</span><span class="nf">markAsVisited</span><span class="p">(</span><span class="n">sl</span><span class="o">.</span><span class="n">rect</span><span class="p">)</span> <span class="c1">// Do not check neighborhood of this slice if it has no contour.</span> <span class="k">if</span> <span class="n">sl</span><span class="o">.</span><span class="nf">hasContour</span><span class="p">()</span> <span class="o">==</span> <span class="kc">false</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span> <span class="c1">// Part of a contour found in this slice, store it and search further.</span> <span class="n">contour</span><span class="o">.</span><span class="nf">add</span><span class="p">(</span><span class="n">sl</span><span class="p">)</span> <span class="c1">// Calculate boundary of the neighborhood</span> <span class="k">let</span> <span class="nv">frCol</span> <span class="o">=</span> <span class="nf">max</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">sl</span><span class="o">.</span><span class="n">rect</span><span class="o">.</span><span class="n">minX</span> <span class="o">-</span> <span class="n">sliceSize</span><span class="o">.</span><span class="n">width</span><span class="p">)</span> <span class="k">let</span> <span class="nv">toCol</span> <span class="o">=</span> <span class="nf">min</span><span class="p">(</span><span class="n">sl</span><span class="o">.</span><span class="n">rect</span><span class="o">.</span><span class="n">minX</span> <span class="o">+</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">sliceSize</span><span class="o">.</span><span class="n">width</span><span class="p">,</span> <span class="n">imageSize</span><span class="o">.</span><span class="n">width</span> <span class="o">-</span> <span class="n">sliceSize</span><span class="o">.</span><span class="n">width</span><span class="p">)</span> <span class="k">let</span> <span class="nv">frRow</span> <span class="o">=</span> <span class="nf">max</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">sl</span><span class="o">.</span><span class="n">rect</span><span class="o">.</span><span class="n">minY</span> <span class="o">-</span> <span class="n">sliceSize</span><span class="o">.</span><span class="n">height</span><span class="p">)</span> <span class="k">let</span> <span class="nv">toRow</span> <span class="o">=</span> <span class="nf">min</span><span class="p">(</span><span class="n">sl</span><span class="o">.</span><span class="n">rect</span><span class="o">.</span><span class="n">minY</span> <span class="o">+</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">sliceSize</span><span class="o">.</span><span class="n">height</span><span class="p">,</span> <span class="n">imageSize</span><span class="o">.</span><span class="n">height</span> <span class="o">-</span> <span class="n">sliceSize</span><span class="o">.</span><span class="n">height</span><span class="p">)</span> <span class="c1">// Iterate through neighborhood slices</span> <span class="k">for</span> <span class="n">col</span> <span class="k">in</span> <span class="n">frCol</span><span class="o">.</span><span class="nf">stride</span><span class="p">(</span><span class="nv">to</span><span class="p">:</span> <span class="n">toCol</span><span class="p">,</span> <span class="nv">by</span><span class="p">:</span> <span class="n">sliceSize</span><span class="o">.</span><span class="n">width</span><span class="p">)</span> <span class="p">{</span> <span class="k">for</span> <span class="n">row</span> <span class="k">in</span> <span class="n">frRow</span><span class="o">.</span><span class="nf">stride</span><span class="p">(</span><span class="nv">to</span><span class="p">:</span> <span class="n">toRow</span><span class="p">,</span> <span class="nv">by</span><span class="p">:</span> <span class="n">sliceSize</span><span class="o">.</span><span class="n">height</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// do not check the center slice (the same as `sl`)</span> <span class="k">if</span> <span class="n">row</span> <span class="o">==</span> <span class="n">sl</span><span class="o">.</span><span class="n">rect</span><span class="o">.</span><span class="n">minX</span> <span class="o">&amp;&amp;</span> <span class="n">col</span> <span class="o">==</span> <span class="n">sl</span><span class="o">.</span><span class="n">rect</span><span class="o">.</span><span class="n">minY</span> <span class="p">{</span> <span class="k">continue</span> <span class="p">}</span> <span class="k">let</span> <span class="nv">rect</span> <span class="o">=</span> <span class="kt">CGRect</span><span class="p">(</span><span class="nv">x</span><span class="p">:</span> <span class="n">col</span><span class="p">,</span> <span class="nv">y</span><span class="p">:</span> <span class="n">row</span><span class="p">,</span> <span class="nv">width</span><span class="p">:</span> <span class="n">sliceSize</span><span class="o">.</span><span class="n">width</span><span class="p">,</span> <span class="nv">height</span><span class="p">:</span> <span class="n">sliceSize</span><span class="o">.</span><span class="n">height</span><span class="p">)</span> <span class="k">let</span> <span class="nv">nearSlice</span> <span class="o">=</span> <span class="kt">Slice</span><span class="p">(</span><span class="nv">rect</span><span class="p">:</span> <span class="n">rect</span><span class="p">,</span> <span class="nv">delegate</span><span class="p">:</span> <span class="k">self</span><span class="p">)</span> <span class="c1">// Check its neighborhood</span> <span class="nf">searchNeighborhood</span><span class="p">(</span><span class="o">&amp;</span><span class="n">contour</span><span class="p">,</span> <span class="nv">slice</span><span class="p">:</span> <span class="n">nearSlice</span><span class="p">)</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p><em>In the same time you’re examinating slices you have to remember to mark them as visited to not make your algorithm running forever. Also, remember to calculate the common area for all grouped slices.</em></p> <h3 id="cutting-and-resizing-contours">Cutting and resizing contours</h3> <p>After contour is found algorithm uses <strong>output size</strong> parameter defined above to resize contour and save it on disk.</p> <p>Cutting part is easy and look like below</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">let</span> <span class="nv">rect</span> <span class="o">=</span> <span class="n">contour</span><span class="o">.</span><span class="n">rect</span> <span class="k">if</span> <span class="k">let</span> <span class="nv">subImage</span> <span class="o">=</span> <span class="kt">CGImageCreateWithImageInRect</span><span class="p">(</span><span class="n">image</span><span class="p">,</span> <span class="n">rect</span><span class="p">)</span> <span class="p">{</span> <span class="k">let</span> <span class="nv">sourceImage</span> <span class="o">=</span> <span class="kt">NSImage</span><span class="p">(</span><span class="kt">CGImage</span><span class="p">:</span> <span class="n">subImage</span><span class="p">,</span> <span class="nv">size</span><span class="p">:</span> <span class="n">rect</span><span class="o">.</span><span class="n">size</span><span class="p">)</span> <span class="k">return</span> <span class="kt">ImageResizer</span><span class="o">.</span><span class="nf">resize</span><span class="p">(</span><span class="n">sourceImage</span><span class="p">,</span> <span class="nv">toSize</span><span class="p">:</span> <span class="n">outputSize</span><span class="p">)</span> <span class="p">}</span></code></pre></figure> <p>Image we cut might have different size that defined output size. Resizing it consists of finding longer edge and resizing it to desired one by keeping ratio of cutted piece of a scan.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="c1">// source - cutted image with contour</span> <span class="c1">// resultSize - output size</span> <span class="k">if</span> <span class="n">source</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">height</span> <span class="o">&gt;</span> <span class="n">source</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">width</span> <span class="p">{</span> <span class="k">let</span> <span class="nv">ratio</span> <span class="o">=</span> <span class="n">source</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">height</span> <span class="o">/</span> <span class="n">source</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">width</span> <span class="n">drawRect</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">height</span> <span class="o">=</span> <span class="n">resultSize</span><span class="o">.</span><span class="n">height</span> <span class="n">drawRect</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">width</span> <span class="o">=</span> <span class="n">drawRect</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">height</span> <span class="o">/</span> <span class="n">ratio</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="k">let</span> <span class="nv">ratio</span> <span class="o">=</span> <span class="n">source</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">width</span> <span class="o">/</span> <span class="n">source</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">height</span> <span class="n">drawRect</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">width</span> <span class="o">=</span> <span class="n">resultSize</span><span class="o">.</span><span class="n">width</span> <span class="n">drawRect</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">height</span> <span class="o">=</span> <span class="n">drawRect</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">width</span> <span class="o">/</span> <span class="n">ratio</span> <span class="p">}</span></code></pre></figure> <p>Later the contour have to be centered on the output image.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="n">drawRect</span><span class="o">.</span><span class="n">origin</span><span class="o">.</span><span class="n">x</span> <span class="o">=</span> <span class="p">(</span><span class="n">resultSize</span><span class="o">.</span><span class="n">width</span> <span class="o">-</span> <span class="n">drawRect</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">width</span><span class="p">)</span> <span class="o">*</span> <span class="mf">0.5</span> <span class="n">drawRect</span><span class="o">.</span><span class="n">origin</span><span class="o">.</span><span class="n">y</span> <span class="o">=</span> <span class="p">(</span><span class="n">resultSize</span><span class="o">.</span><span class="n">height</span> <span class="o">-</span> <span class="n">drawRect</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">height</span><span class="p">)</span> <span class="o">*</span> <span class="mf">0.5</span></code></pre></figure> <p>Then algorithm creates image with white background and draws contour on it.</p> <h3 id="preview-of-what-algorithm-found">Preview of what algorithm found</h3> <p>As I mentioned above it is useful to see what algorithm sees. Having all the information above contours and slices the preview can be drawn.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kt">CGContextSetLineWidth</span><span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">)</span> <span class="kt">CGContextTranslateCTM</span><span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="kt">CGFloat</span><span class="p">(</span><span class="n">imageSize</span><span class="o">.</span><span class="n">height</span><span class="p">));</span> <span class="kt">CGContextScaleCTM</span><span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">,</span> <span class="o">-</span><span class="mf">1.0</span><span class="p">)</span> <span class="k">for</span> <span class="n">contour</span> <span class="k">in</span> <span class="n">contours</span> <span class="p">{</span> <span class="c1">// draw contour's rect</span> <span class="kt">CGContextSetStrokeColorWithColor</span><span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="kt">NSColor</span><span class="o">.</span><span class="nf">blueColor</span><span class="p">()</span><span class="o">.</span><span class="kt">CGColor</span><span class="p">)</span> <span class="k">for</span> <span class="n">slRect</span> <span class="k">in</span> <span class="n">contour</span><span class="o">.</span><span class="n">sliceRects</span> <span class="p">{</span> <span class="c1">// draw slice rect</span> <span class="kt">CGContextStrokeRect</span><span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="n">slRect</span><span class="p">)</span> <span class="p">}</span> <span class="kt">CGContextSetStrokeColorWithColor</span><span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="kt">NSColor</span><span class="o">.</span><span class="nf">redColor</span><span class="p">()</span><span class="o">.</span><span class="kt">CGColor</span><span class="p">)</span> <span class="kt">CGContextStrokeRect</span><span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="n">contour</span><span class="o">.</span><span class="n">rect</span><span class="p">)</span> <span class="p">}</span></code></pre></figure> <p>The interesting part of generating such preview is that we can see how algorithm accuracy increases when slice width gets smaller.</p> <p>Following animation presents how algorithm accuracy look like on 540x562 image with 100 ones on it. Width of a slice is 20 and goes down to 2. The smaller it gets you can see that slices forms digit one.</p> <p><img src="/uploads/post-51/animation.gif" alt="animation" /></p> <p>Not always the smallest size of a slice is the best option to follow. The drawbacks are the algorithm gets slower and slower and can even grab some dots or other small stains from a scan and interpret them as contours. We could have another parameter that would say whether contour should be omitted or not.</p> <h3 id="performance">Performance</h3> <p>At some point of development the algorithm ended up in 30 seconds to detect contours on 540x562 image with width slice of 5px. As <a href="https://pl.linkedin.com/in/piotrtobolski">Piotr Tobolski</a> pointed out it was too slow for such a small number of operations - about ~600,000 operations (preparing data and examining pixels).</p> <p>The thing I didn’t remember of was to change <strong>Optimization Level</strong> of <strong>Swift Compiler Code Generation</strong> from <em>None</em> <strong>[-Onone]</strong> to <em>Fast</em> <strong>[-O]</strong>. After I changed it to <em>Fast</em> I started getting 1.22 seconds for the same operations.</p> <h3 id="wrap-up">Wrap-up</h3> <p>This is the first post of series about machine learning. As I finished the online course that took me few months I would like to start learning more in this area and start experimenting.</p> <p>The algorithm accuracy for iPhone photos works pretty well but I believe scans would work even betters. The photos are more grey-and-black than white-and-black. You can play with <em>threshold</em>, edit photo in Preview.app to make it more white-and-black or just scan it and it should be ready to use.</p> <p>Here we covered how you can get a handwritten digits data to feed the neural network later. If you’d like to learn what machine learning is and how to start, you can go to the <a href="https://www.coursera.org/learn/machine-learning">coursera course</a> and after you finished it, you can take a look on <a href="http://neuralnetworksanddeeplearning.com/index.html">Neural Networks and Deep Learning</a> book that covers a lot of details about neural networks for digits recognition.</p> Sat, 18 Jun 2016 00:00:00 +0200 http://szulctomasz.com/2016/06/18/Finding-contours-on-paper-sheet-scans-in-swift.html http://szulctomasz.com/2016/06/18/Finding-contours-on-paper-sheet-scans-in-swift.html osx, machine learning, swift iOS: Prototyping button-like control with nice animations <p>I browsed <a href="http://dribble.com">dribbble.com</a> again and found cool UI control. <em>Unfortunately I cannot find the resource again on dribbble</em> - It just disappeared and I do not have any link to it. Anyway, here is the final effect very similar to what I found.</p> <p><strong>Operation succeeded and failed</strong></p> <p><img src="/uploads/post-50/gif-2.gif" alt="final-effect-2" /> <img src="/uploads/post-50/gif-3.gif" alt="final-effect-3" /></p> <p><strong>Touching down and disabling/enabling</strong></p> <p><img src="/uploads/post-50/gif-1.gif" alt="final-effect-1" /> <img src="/uploads/post-50/gif-4.gif" alt="final-effect-4" /></p> <p>Looking nice, ha? You can find the project on <a href="https://github.com/tomkowz/transitioning-button-demo">tomkowz/transitioning-button-demo</a>.</p> <h3 id="how-does-it-work">How does it work?</h3> <p>The logic behind the control is easy to understand. The button has 4 states.</p> <ul> <li><strong>Begin</strong> - Blue button with just <em>Submit</em> title. You can push it down and it will nicely be pushed deeper and colors will be inverted</li> <li><strong>Loading</strong> - After button is pushed down and then released it gets back to the previous appearance and animates to show a spinner. This is good time to do e.g. network requests, submitting a form, etc.. - Please notice how button dimension is changing to create circle with the spinner - This is why I like it.</li> <li><strong>Finish with success</strong> - After you did your work and everything finished with success, you can change to this state to show some success message and style the button little bit differently. Here I used darker blue.</li> <li><strong>Finish with failure</strong> - If something went wrong you can style the button to indicate that operation failed. A bit later state of the button goes back to <em>Begin</em> so you can tap it again and re-try an operation.</li> </ul> <h3 id="view-hierarchy">View hierarchy</h3> <p>Not sure whether this is designed in an optimal way or not. Probably something could be simplified. If you find such thing, I’d be glad to know it.</p> <p><img src="/uploads/post-50/schema.png" alt="schema" /></p> <p>Schema explained below.</p> <ul> <li><strong>1</strong> - Main view for the control.</li> <li><strong>2</strong> - I called this <em>placeholder view</em> but this is not the correct name. This view have constraint that specifies width of the control and do not have any other subview in it. Every subview is added to <strong>1</strong>. This view is hidden.</li> <li><strong>3</strong>, <strong>4</strong>, <strong>5</strong> - Each of these represent another state of the control. The first one is label presented for <strong>Begin</strong> state, the second contains spinner and the third contains success/failure message. Each of these has also the same height as the <strong>1</strong> - It ensure that subviews of <strong>3 - 5</strong> are correctly centered both vertically and horizontally while presented on screen. Each of these are horizontally centered in <strong>1</strong>.</li> <li><strong>5</strong> - This view does not have constraint to the bottom of the <strong>1</strong>.</li> <li><strong>6</strong> - This is constraint between top of the <strong>1</strong> and top of the <strong>3</strong>. It allows to easily scroll subviews during state changes.</li> <li><strong>7</strong> - As mentioned above, this is a width constraint of the control.</li> </ul> <h3 id="implementation">Implementation</h3> <p>The control has 8 properties that define colors of texts and background for a specific appearance style - <em>Normal</em>, <em>Pressed</em>, <em>Disabled</em>, <em>Success</em>, <em>Failure</em>.</p> <p>When state changes view appearance is changing.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">class</span> <span class="kt">TransitioningButton</span><span class="p">:</span> <span class="kt">UIControl</span> <span class="p">{</span> <span class="kd">enum</span> <span class="kt">State</span> <span class="p">{</span> <span class="k">case</span> <span class="kt">Begin</span> <span class="k">case</span> <span class="kt">Loading</span> <span class="k">case</span> <span class="kt">FinishWithSuccess</span> <span class="k">case</span> <span class="kt">FinishWithFailure</span> <span class="p">}</span> <span class="kd">@IBInspectable</span> <span class="k">var</span> <span class="nv">normalBackgroundColor</span><span class="p">:</span> <span class="kt">UIColor</span><span class="p">?</span> <span class="kd">@IBInspectable</span> <span class="k">var</span> <span class="nv">pressedBackgroundColor</span><span class="p">:</span> <span class="kt">UIColor</span><span class="p">?</span> <span class="kd">@IBInspectable</span> <span class="k">var</span> <span class="nv">disabledBackgroundColor</span><span class="p">:</span> <span class="kt">UIColor</span><span class="p">?</span> <span class="kd">@IBInspectable</span> <span class="k">var</span> <span class="nv">disabledTextColor</span><span class="p">:</span> <span class="kt">UIColor</span><span class="p">?</span> <span class="kd">@IBInspectable</span> <span class="k">var</span> <span class="nv">successBackgroundColor</span><span class="p">:</span> <span class="kt">UIColor</span><span class="p">?</span> <span class="kd">@IBInspectable</span> <span class="k">var</span> <span class="nv">successTextColor</span><span class="p">:</span> <span class="kt">UIColor</span><span class="p">?</span> <span class="kd">@IBInspectable</span> <span class="k">var</span> <span class="nv">failureBackgroundColor</span><span class="p">:</span> <span class="kt">UIColor</span><span class="p">?</span> <span class="kd">@IBInspectable</span> <span class="k">var</span> <span class="nv">failureTextColor</span><span class="p">:</span> <span class="kt">UIColor</span><span class="p">?</span> <span class="kd">@IBOutlet</span> <span class="k">var</span> <span class="nv">firstLabel</span><span class="p">:</span> <span class="kt">UILabel</span><span class="o">!</span> <span class="kd">@IBOutlet</span> <span class="k">var</span> <span class="nv">activityIndicator</span><span class="p">:</span> <span class="kt">UIActivityIndicatorView</span><span class="o">!</span> <span class="kd">@IBOutlet</span> <span class="k">var</span> <span class="nv">secondLabel</span><span class="p">:</span> <span class="kt">UILabel</span><span class="o">!</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">containerBegin</span><span class="p">:</span> <span class="kt">UIView</span><span class="o">!</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">containerLoading</span><span class="p">:</span> <span class="kt">UIView</span><span class="o">!</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">containerFinish</span><span class="p">:</span> <span class="kt">UIView</span><span class="o">!</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">topConstraint</span><span class="p">:</span> <span class="kt">NSLayoutConstraint</span><span class="o">!</span> <span class="kd">@IBOutlet</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">widthConstriant</span><span class="p">:</span> <span class="kt">NSLayoutConstraint</span><span class="o">!</span> <span class="k">var</span> <span class="nv">buttonState</span><span class="p">:</span> <span class="kt">State</span> <span class="o">=</span> <span class="o">.</span><span class="kt">Begin</span> <span class="p">{</span> <span class="k">didSet</span> <span class="p">{</span> <span class="nf">updateAfterButtonStateChanged</span><span class="p">(</span><span class="k">self</span><span class="o">.</span><span class="n">buttonState</span><span class="p">)</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>Added listeners for <em>TouchUpInside</em>, <em>TouchUpOutside</em>, <em>TouchDown</em> control events, so style of tapping/pressing button and releasing it can be easily updated.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">private</span> <span class="kd">func</span> <span class="nf">configureActions</span><span class="p">()</span> <span class="p">{</span> <span class="k">self</span><span class="o">.</span><span class="nf">addTarget</span><span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="nv">action</span><span class="p">:</span> <span class="kd">#selector(</span><span class="nf">touchUpInside</span><span class="kd">)</span><span class="p">,</span> <span class="nv">forControlEvents</span><span class="p">:</span> <span class="o">.</span><span class="kt">TouchUpInside</span><span class="p">)</span> <span class="k">self</span><span class="o">.</span><span class="nf">addTarget</span><span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="nv">action</span><span class="p">:</span> <span class="kd">#selector(</span><span class="nf">touchUpOutside</span><span class="kd">)</span><span class="p">,</span> <span class="nv">forControlEvents</span><span class="p">:</span> <span class="o">.</span><span class="kt">TouchUpOutside</span><span class="p">)</span> <span class="k">self</span><span class="o">.</span><span class="nf">addTarget</span><span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="nv">action</span><span class="p">:</span> <span class="kd">#selector(</span><span class="nf">touchDown</span><span class="kd">)</span><span class="p">,</span> <span class="nv">forControlEvents</span><span class="p">:</span> <span class="o">.</span><span class="kt">TouchDown</span><span class="p">)</span> <span class="p">}</span> <span class="o">...</span> <span class="kd">private</span> <span class="kd">extension</span> <span class="kt">TransitioningButton</span> <span class="p">{</span> <span class="kd">@objc</span> <span class="kd">private</span> <span class="kd">func</span> <span class="nf">touchUpInside</span><span class="p">()</span> <span class="p">{</span> <span class="nf">pushOut</span><span class="p">()</span> <span class="p">}</span> <span class="kd">@objc</span> <span class="kd">private</span> <span class="kd">func</span> <span class="nf">touchUpOutside</span><span class="p">()</span> <span class="p">{</span> <span class="nf">pushOut</span><span class="p">()</span> <span class="p">}</span> <span class="kd">@objc</span> <span class="kd">private</span> <span class="kd">func</span> <span class="nf">touchDown</span><span class="p">()</span> <span class="p">{</span> <span class="nf">pushIn</span><span class="p">()</span> <span class="p">}</span> <span class="kd">private</span> <span class="kd">func</span> <span class="nf">pushIn</span><span class="p">()</span> <span class="p">{</span> <span class="k">let</span> <span class="nv">pushAnimation</span> <span class="o">=</span> <span class="kt">CABasicAnimation</span><span class="p">(</span><span class="nv">keyPath</span><span class="p">:</span> <span class="s">"transform.scale"</span><span class="p">)</span> <span class="n">pushAnimation</span><span class="o">.</span><span class="n">duration</span> <span class="o">=</span> <span class="mf">0.1</span> <span class="n">pushAnimation</span><span class="o">.</span><span class="n">fromValue</span> <span class="o">=</span> <span class="mi">1</span> <span class="n">pushAnimation</span><span class="o">.</span><span class="n">toValue</span> <span class="o">=</span> <span class="mf">0.95</span> <span class="n">pushAnimation</span><span class="o">.</span><span class="n">timingFunction</span> <span class="o">=</span> <span class="kt">CAMediaTimingFunction</span><span class="p">(</span><span class="nv">name</span><span class="p">:</span> <span class="n">kCAMediaTimingFunctionEaseOut</span><span class="p">)</span> <span class="n">pushAnimation</span><span class="o">.</span><span class="n">removedOnCompletion</span> <span class="o">=</span> <span class="kc">false</span> <span class="n">pushAnimation</span><span class="o">.</span><span class="n">fillMode</span> <span class="o">=</span> <span class="n">kCAFillModeForwards</span> <span class="k">self</span><span class="o">.</span><span class="n">layer</span><span class="o">.</span><span class="nf">addAnimation</span><span class="p">(</span><span class="n">pushAnimation</span><span class="p">,</span> <span class="nv">forKey</span><span class="p">:</span> <span class="kc">nil</span><span class="p">)</span> <span class="nf">updateAppearanceToPressed</span><span class="p">()</span> <span class="p">}</span> <span class="kd">private</span> <span class="kd">func</span> <span class="nf">pushOut</span><span class="p">()</span> <span class="p">{</span> <span class="nf">updateAppearanceToNormal</span><span class="p">()</span> <span class="k">let</span> <span class="nv">pushAnimation</span> <span class="o">=</span> <span class="kt">CABasicAnimation</span><span class="p">(</span><span class="nv">keyPath</span><span class="p">:</span> <span class="s">"transform.scale"</span><span class="p">)</span> <span class="n">pushAnimation</span><span class="o">.</span><span class="n">duration</span> <span class="o">=</span> <span class="mf">0.1</span> <span class="n">pushAnimation</span><span class="o">.</span><span class="n">fromValue</span> <span class="o">=</span> <span class="mf">0.95</span> <span class="n">pushAnimation</span><span class="o">.</span><span class="n">toValue</span> <span class="o">=</span> <span class="mi">1</span> <span class="n">pushAnimation</span><span class="o">.</span><span class="n">timingFunction</span> <span class="o">=</span> <span class="kt">CAMediaTimingFunction</span><span class="p">(</span><span class="nv">name</span><span class="p">:</span> <span class="n">kCAMediaTimingFunctionEaseOut</span><span class="p">)</span> <span class="n">pushAnimation</span><span class="o">.</span><span class="n">removedOnCompletion</span> <span class="o">=</span> <span class="kc">false</span> <span class="n">pushAnimation</span><span class="o">.</span><span class="n">fillMode</span> <span class="o">=</span> <span class="n">kCAFillModeForwards</span> <span class="k">self</span><span class="o">.</span><span class="n">layer</span><span class="o">.</span><span class="nf">addAnimation</span><span class="p">(</span><span class="n">pushAnimation</span><span class="p">,</span> <span class="nv">forKey</span><span class="p">:</span> <span class="kc">nil</span><span class="p">)</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>Here are two animations that changes scale of a button, so it imitate pushing it and creates some depth on the screen.</p> <p>The next important thing is a method which is responsible for animating state changes.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">private</span> <span class="kd">func</span> <span class="nf">updateAfterButtonStateChanged</span><span class="p">(</span><span class="nv">state</span><span class="p">:</span> <span class="kt">State</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// Get container view that will be presented for new state</span> <span class="k">let</span> <span class="nv">containerView</span> <span class="o">=</span> <span class="nf">containerViewForState</span><span class="p">(</span><span class="n">state</span><span class="p">)</span> <span class="cm">/* As the content might change in the same autolayout pass as this animation We have to make sure that UI is ready to be presented. This is especially important when you're about to present short or long text in case when previous text was oposite, meaning, you had short text and not it is long or vice-versa. Without doing this additional pass you'd see that text is moving up and left or right, depending whether new text it is longer or shorter. */</span> <span class="k">let</span> <span class="nv">loopUntil</span> <span class="o">=</span> <span class="kt">NSDate</span><span class="p">(</span><span class="nv">timeIntervalSinceNow</span><span class="p">:</span> <span class="mi">0</span><span class="p">)</span> <span class="kt">NSRunLoop</span><span class="o">.</span><span class="nf">currentRunLoop</span><span class="p">()</span><span class="o">.</span><span class="nf">runMode</span><span class="p">(</span><span class="kt">NSDefaultRunLoopMode</span><span class="p">,</span> <span class="nv">beforeDate</span><span class="p">:</span> <span class="n">loopUntil</span><span class="p">)</span> <span class="c1">// Calculate point to where scroll view should offset its content</span> <span class="k">let</span> <span class="nv">scrollToY</span> <span class="o">=</span> <span class="nf">offsetForState</span><span class="p">(</span><span class="n">state</span><span class="p">)</span> <span class="c1">// Decide whether button should react on touches...</span> <span class="n">userInteractionEnabled</span> <span class="o">=</span> <span class="n">state</span> <span class="o">==</span> <span class="o">.</span><span class="kt">Begin</span> <span class="c1">// Set new width for new state</span> <span class="k">self</span><span class="o">.</span><span class="n">widthConstriant</span><span class="o">.</span><span class="n">constant</span> <span class="o">=</span> <span class="nf">max</span><span class="p">(</span><span class="k">self</span><span class="o">.</span><span class="n">bounds</span><span class="o">.</span><span class="n">height</span><span class="p">,</span> <span class="n">containerView</span><span class="o">.</span><span class="n">subviews</span><span class="o">.</span><span class="n">first</span><span class="o">!.</span><span class="n">bounds</span><span class="o">.</span><span class="n">width</span> <span class="o">+</span> <span class="p">(</span><span class="k">self</span><span class="o">.</span><span class="n">bounds</span><span class="o">.</span><span class="n">height</span> <span class="o">/</span> <span class="mf">2.0</span><span class="p">))</span> <span class="c1">// And animate the change...</span> <span class="kt">UIView</span><span class="o">.</span><span class="nf">animateWithDuration</span><span class="p">(</span><span class="mf">0.2</span><span class="p">,</span> <span class="nv">delay</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">options</span><span class="p">:</span> <span class="o">.</span><span class="kt">CurveEaseOut</span><span class="p">,</span> <span class="nv">animations</span><span class="p">:</span> <span class="p">{</span> <span class="k">self</span><span class="o">.</span><span class="n">topConstraint</span><span class="o">.</span><span class="n">constant</span> <span class="o">=</span> <span class="o">-</span><span class="n">scrollToY</span><span class="p">;</span> <span class="k">self</span><span class="o">.</span><span class="nf">layoutIfNeeded</span><span class="p">()</span> <span class="c1">// Update style of the control</span> <span class="k">if</span> <span class="n">state</span> <span class="o">==</span> <span class="o">.</span><span class="kt">Begin</span> <span class="p">{</span> <span class="k">if</span> <span class="k">self</span><span class="o">.</span><span class="n">enabled</span> <span class="p">{</span> <span class="k">self</span><span class="o">.</span><span class="nf">updateAppearanceToNormal</span><span class="p">()</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="k">self</span><span class="o">.</span><span class="nf">updateAppearanceToDisabled</span><span class="p">()</span> <span class="p">}</span> <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="n">state</span> <span class="o">==</span> <span class="o">.</span><span class="kt">FinishWithSuccess</span> <span class="p">{</span> <span class="k">self</span><span class="o">.</span><span class="nf">updateAppearanceToSuccess</span><span class="p">()</span> <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="n">state</span> <span class="o">==</span> <span class="o">.</span><span class="kt">FinishWithFailure</span> <span class="p">{</span> <span class="k">self</span><span class="o">.</span><span class="nf">updateAppearanceToFailure</span><span class="p">()</span> <span class="p">}</span> <span class="p">},</span> <span class="nv">completion</span><span class="p">:</span> <span class="p">{</span> <span class="n">_</span> <span class="k">in</span> <span class="c1">// If an operation performed by this control failed, it will</span> <span class="c1">// show the control in the .Begin state.</span> <span class="k">if</span> <span class="n">state</span> <span class="o">==</span> <span class="o">.</span><span class="kt">FinishWithFailure</span> <span class="p">{</span> <span class="k">let</span> <span class="nv">time</span> <span class="o">=</span> <span class="nf">dispatch_time</span><span class="p">(</span><span class="kt">DISPATCH_TIME_NOW</span><span class="p">,</span> <span class="kt">Int64</span><span class="p">(</span><span class="mi">1</span> <span class="o">*</span> <span class="kt">Double</span><span class="p">(</span><span class="kt">NSEC_PER_SEC</span><span class="p">)))</span> <span class="nf">dispatch_after</span><span class="p">(</span><span class="n">time</span><span class="p">,</span> <span class="nf">dispatch_get_main_queue</span><span class="p">(),</span> <span class="p">{</span> <span class="k">let</span> <span class="nv">transition</span> <span class="o">=</span> <span class="kt">CATransition</span><span class="p">()</span> <span class="n">transition</span><span class="o">.</span><span class="n">type</span> <span class="o">=</span> <span class="n">kCATransitionFade</span> <span class="n">transition</span><span class="o">.</span><span class="n">duration</span> <span class="o">=</span> <span class="mf">0.3</span> <span class="k">self</span><span class="o">.</span><span class="n">layer</span><span class="o">.</span><span class="nf">addAnimation</span><span class="p">(</span><span class="n">transition</span><span class="p">,</span> <span class="nv">forKey</span><span class="p">:</span> <span class="n">kCATransition</span><span class="p">)</span> <span class="k">self</span><span class="o">.</span><span class="nf">updateAfterButtonStateChanged</span><span class="p">(</span><span class="o">.</span><span class="kt">Begin</span><span class="p">)</span> <span class="p">})</span> <span class="p">}</span> <span class="p">})</span> <span class="p">}</span></code></pre></figure> <p>Let’s take a look what’s going on here.</p> <p>At the beginning we need to obtain container with a label/spinner that will be presented after state did change.</p> <p>The next two lines are interesting.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">let</span> <span class="nv">loopUntil</span> <span class="o">=</span> <span class="kt">NSDate</span><span class="p">(</span><span class="nv">timeIntervalSinceNow</span><span class="p">:</span> <span class="mi">0</span><span class="p">)</span> <span class="kt">NSRunLoop</span><span class="o">.</span><span class="nf">currentRunLoop</span><span class="p">()</span><span class="o">.</span><span class="nf">runMode</span><span class="p">(</span><span class="kt">NSDefaultRunLoopMode</span><span class="p">,</span> <span class="nv">beforeDate</span><span class="p">:</span> <span class="n">loopUntil</span><span class="p">)</span></code></pre></figure> <p>I noticed this:</p> <p>Changing text and animating entire control (scrolling its content) caused that label (for <em>Final</em> state) was going up-left or up-right depending whether text was changed to long or short.</p> <p>This was caused because container with label was updating its layout in the same pass as entire control was shifting its subviews.</p> <p>Adding these two lines forces another quick pass on the current loop (main thread) so UI gets refreshed and label is correctly positioned. In effect, when animating change state, you can see that longer label is nicely animated keeping its centered position.</p> <p>This should better illustrate the issue. Notice how long label on right is shifting while presenting.</p> <p><img src="/uploads/post-50/gif-5.gif" alt="transition-correct" /> <img src="/uploads/post-50/gif-6.gif" alt="transition-incorrect" /></p> <p>Next thing is to set new width of the control. We need to take width of a subview inside the container and add some margin. The margin is calculated to create a circle when spinner is presented. Labels and spinners have the same leading and trailing distances to the control leading and trailing edges.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">self</span><span class="o">.</span><span class="n">widthConstriant</span><span class="o">.</span><span class="n">constant</span> <span class="o">=</span> <span class="nf">max</span><span class="p">(</span><span class="k">self</span><span class="o">.</span><span class="n">bounds</span><span class="o">.</span><span class="n">height</span><span class="p">,</span> <span class="n">containerView</span><span class="o">.</span><span class="n">subviews</span><span class="o">.</span><span class="n">first</span><span class="o">!.</span><span class="n">bounds</span><span class="o">.</span><span class="n">width</span> <span class="o">+</span> <span class="p">(</span><span class="k">self</span><span class="o">.</span><span class="n">bounds</span><span class="o">.</span><span class="n">height</span> <span class="o">/</span> <span class="mf">2.0</span><span class="p">))</span></code></pre></figure> <p>Next lines of code just changes color of the labels and the background, so nothing interesting.</p> <p>In case when new state is <em>Finish with failure</em> there is <em>CATransition</em> animation added to the control’s <em>layer</em>, so it nicely animates state change to <em>Begin</em> with a fade effect.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">let</span> <span class="nv">transition</span> <span class="o">=</span> <span class="kt">CATransition</span><span class="p">()</span> <span class="n">transition</span><span class="o">.</span><span class="n">type</span> <span class="o">=</span> <span class="n">kCATransitionFade</span> <span class="n">transition</span><span class="o">.</span><span class="n">duration</span> <span class="o">=</span> <span class="mf">0.3</span> <span class="k">self</span><span class="o">.</span><span class="n">layer</span><span class="o">.</span><span class="nf">addAnimation</span><span class="p">(</span><span class="n">transition</span><span class="p">,</span> <span class="nv">forKey</span><span class="p">:</span> <span class="n">kCATransition</span><span class="p">)</span> <span class="k">self</span><span class="o">.</span><span class="nf">updateAfterButtonStateChanged</span><span class="p">(</span><span class="o">.</span><span class="kt">Begin</span><span class="p">)</span></code></pre></figure> Sat, 21 May 2016 00:00:00 +0200 http://szulctomasz.com/2016/05/21/ios-prototyping-button-like-control-with-nice-animations.html http://szulctomasz.com/2016/05/21/ios-prototyping-button-like-control-with-nice-animations.html ios, custom control, uicontrol, animations iOS: Custom View Controller Transition by expanding view <p>I was looking for inspirations on <em>dribbble.com</em> and found work that shows nice transition between view controllers by expanding a view (e.g. a button) in every direction. You can find it here: <a href="https://dribbble.com/shots/1945593-Login-Home-Screen">Login &amp; Home Screen</a>.</p> <p>It is more like Android material design style animation, but I think it may fit nicely in any onboarding process animations.</p> <h3 id="the-animation">The animation</h3> <p>Let’s disassemble this animation.</p> <ul> <li>There is a view that is a source of animation. Let’s call it <strong>expandable view</strong>. In this work on dribbble the <strong>expandable view</strong> view is a “Sign in” button. (I’ll simplify it and just use button that is ready to be scaled. You could use additional delegate to notify the expandable view that it should prepare for being expanded - It could also implement <em>Expandable</em> protocol or something similar)</li> <li>When user taps the button, the first step is to change the appearance of the <strong>expandable view</strong>. The view is about to scale to fill entire screen, so everthing that would not look good is removed from the view. In this case this is loading spinner and plus sign that is removed from the view. The only thing that will be scaling is simple circle button with solid background.</li> <li>The next step is the scaling process itself. The button expands so it fills entire screen.</li> <li>After view expanded quickly next view controller is presented by simple fade.</li> </ul> <h3 id="custom-transitions-in-ios">Custom transitions in iOS</h3> <p>One way to approach the problem is to create custom transition animation. This can be done by <a href="https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewControllerAnimatedTransitioning_Protocol/">UIViewControllerAnimatedTransitioning</a> and <a href="https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewControllerTransitioningDelegate_protocol/index.html">UIViewControllerTransitioningDelegate</a> protocols.</p> <p><strong>UIViewControllerAnimatedTransitioning</strong> - The protocol is used by animator, the class that knows how the animation look like.</p> <p><strong>UIViewControllerTransitioningDelegate</strong> - The protocol is used by transitioning delegate. It knows what is the total length of the animation and can instantiate and return animators for both presenting and dismissing view controllers.</p> <p><em>In this case I’ll only care about presenting animation.</em></p> <h3 id="example-project">Example project</h3> <p>You can find demo project in <a href="https://github.com/tomkowz/ExpandingViewTransition-Demo">ExpandingViewTransition-Demo repository</a> on github.</p> <h3 id="transitioning-delegate">Transitioning delegate</h3> <p>At first, we have to create transitioning delegate that will return animators for presenting and dismissing view controller (we care only about presenting now).</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">import</span> <span class="kt">UIKit</span> <span class="kd">class</span> <span class="kt">ExpandingViewTransition</span><span class="p">:</span> <span class="kt">NSObject</span><span class="p">,</span> <span class="kt">UIViewControllerTransitioningDelegate</span> <span class="p">{</span> <span class="kd">private</span> <span class="k">let</span> <span class="nv">expandableView</span><span class="p">:</span> <span class="kt">UIView</span> <span class="kd">private</span> <span class="k">let</span> <span class="nv">expandViewAnimationDuration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span> <span class="kd">private</span> <span class="k">let</span> <span class="nv">presentVCAnimationDuration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span> <span class="nf">init</span><span class="p">(</span><span class="nv">expandingView</span><span class="p">:</span> <span class="kt">UIView</span><span class="p">,</span> <span class="nv">expandViewAnimationDuration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span> <span class="o">=</span> <span class="mf">0.35</span><span class="p">,</span> <span class="nv">presentVCAnimationDuration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span> <span class="o">=</span> <span class="mf">0.35</span><span class="p">)</span> <span class="p">{</span> <span class="k">self</span><span class="o">.</span><span class="n">expandableView</span> <span class="o">=</span> <span class="n">expandingView</span> <span class="k">self</span><span class="o">.</span><span class="n">expandViewAnimationDuration</span> <span class="o">=</span> <span class="n">expandViewAnimationDuration</span> <span class="k">self</span><span class="o">.</span><span class="n">presentVCAnimationDuration</span> <span class="o">=</span> <span class="n">presentVCAnimationDuration</span> <span class="p">}</span> <span class="kd">func</span> <span class="nf">animationControllerForPresentedController</span><span class="p">(</span> <span class="nv">presented</span><span class="p">:</span> <span class="kt">UIViewController</span><span class="p">,</span> <span class="n">presentingController</span> <span class="nv">presenting</span><span class="p">:</span> <span class="kt">UIViewController</span><span class="p">,</span> <span class="n">sourceController</span> <span class="nv">source</span><span class="p">:</span> <span class="kt">UIViewController</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">UIViewControllerAnimatedTransitioning</span><span class="p">?</span> <span class="p">{</span> <span class="k">return</span> <span class="kt">ExpandingViewTransitionAnimatorPresent</span><span class="p">(</span><span class="nv">expandableView</span><span class="p">:</span> <span class="k">self</span><span class="o">.</span><span class="n">expandableView</span><span class="p">,</span> <span class="nv">expandViewAnimationDuration</span><span class="p">:</span> <span class="k">self</span><span class="o">.</span><span class="n">expandViewAnimationDuration</span><span class="p">,</span> <span class="nv">presentVCAnimationDuration</span><span class="p">:</span> <span class="k">self</span><span class="o">.</span><span class="n">presentVCAnimationDuration</span><span class="p">)</span> <span class="p">}</span> <span class="kd">func</span> <span class="nf">animationControllerForDismissedController</span><span class="p">(</span> <span class="nv">dismissed</span><span class="p">:</span> <span class="kt">UIViewController</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">UIViewControllerAnimatedTransitioning</span><span class="p">?</span> <span class="p">{</span> <span class="k">return</span> <span class="kc">nil</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>The <code class="highlighter-rouge">init(expandableView:expandViewAnimationDuration:presentVCAnimationDuration:)</code> takes reference to view that will be expanded (button), and animation durations that specifies how long expanding process is and how long is fade of destination view controller after <em>expandable view</em> is fully expanded.</p> <p>Two remaining methods are the one from <em>UIViewControllerTransitioningDelegate</em>. First returns <em>ExpandingViewTransitionAnimatorPresent</em> animator which is used to present new view controller.</p> <p>Second method returns <em>nil</em> as we don’t care about dismissing animation. Default dismiss animation will be used in this case.</p> <h3 id="animator">Animator</h3> <p>The foundation of animator look like this.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">class</span> <span class="kt">ExpandingViewTransitionAnimatorPresent</span><span class="p">:</span> <span class="kt">NSObject</span><span class="p">,</span> <span class="kt">UIViewControllerAnimatedTransitioning</span> <span class="p">{</span> <span class="kd">private</span> <span class="k">let</span> <span class="nv">expandableView</span><span class="p">:</span> <span class="kt">UIView</span> <span class="kd">private</span> <span class="k">let</span> <span class="nv">expandViewAnimationDuration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span> <span class="kd">private</span> <span class="k">let</span> <span class="nv">presentVCAnimationDuration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span> <span class="nf">init</span> <span class="p">(</span><span class="nv">expandableView</span><span class="p">:</span> <span class="kt">UIView</span><span class="p">,</span> <span class="nv">expandViewAnimationDuration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">presentVCAnimationDuration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span> <span class="p">{</span> <span class="k">self</span><span class="o">.</span><span class="n">expandableView</span> <span class="o">=</span> <span class="n">expandableView</span> <span class="k">self</span><span class="o">.</span><span class="n">expandViewAnimationDuration</span> <span class="o">=</span> <span class="n">expandViewAnimationDuration</span> <span class="k">self</span><span class="o">.</span><span class="n">presentVCAnimationDuration</span> <span class="o">=</span> <span class="n">presentVCAnimationDuration</span> <span class="p">}</span> <span class="kd">func</span> <span class="nf">transitionDuration</span><span class="p">(</span><span class="nv">transitionContext</span><span class="p">:</span> <span class="kt">UIViewControllerContextTransitioning</span><span class="p">?)</span> <span class="o">-&gt;</span> <span class="kt">NSTimeInterval</span> <span class="p">{</span> <span class="k">return</span> <span class="k">self</span><span class="o">.</span><span class="n">expandViewAnimationDuration</span> <span class="o">+</span> <span class="k">self</span><span class="o">.</span><span class="n">presentVCAnimationDuration</span> <span class="p">}</span> <span class="kd">func</span> <span class="nf">animateTransition</span><span class="p">(</span><span class="nv">transitionContext</span><span class="p">:</span> <span class="kt">UIViewControllerContextTransitioning</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// ...</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>The first thing is to calculate how much the <em>expandable view</em> should expand. The idea is simple. We need to check how far the view is from the edges of the screen, take the longest distance and use it to calculate scale that should be applied.</p> <p>After we find such maximal offset, then we look for the biggest dimension of the <em>expandable view</em> and for longest dimension of the source view controller which should be full screen - I think we could use dimension of <code class="highlighter-rouge">UIScreen.mainScreen().bounds</code> too. It might work even better for some cases.</p> <p>With such informations we’re able to calculate new scale properly.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="cm">/** The method calculates a scale that expandable view should transform to in order to fill entire screen no matter where it is located on the screen. */</span> <span class="kd">private</span> <span class="kd">func</span> <span class="nf">calculateFinalTransformOfExpandingViewInSourceVC</span><span class="p">(</span> <span class="nv">expandingView</span><span class="p">:</span> <span class="kt">UIView</span><span class="p">,</span> <span class="nv">sourceVC</span><span class="p">:</span> <span class="kt">UIViewController</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CGAffineTransform</span> <span class="p">{</span> <span class="c1">// left, right, top, bottom</span> <span class="k">let</span> <span class="nv">offsets</span> <span class="o">=</span> <span class="p">[</span><span class="n">expandingView</span><span class="o">.</span><span class="n">frame</span><span class="o">.</span><span class="n">origin</span><span class="o">.</span><span class="n">x</span><span class="p">,</span> <span class="n">sourceVC</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="n">bounds</span><span class="o">.</span><span class="n">width</span> <span class="o">-</span> <span class="n">expandingView</span><span class="o">.</span><span class="n">frame</span><span class="o">.</span><span class="n">origin</span><span class="o">.</span><span class="n">x</span><span class="p">,</span> <span class="n">expandingView</span><span class="o">.</span><span class="n">frame</span><span class="o">.</span><span class="n">origin</span><span class="o">.</span><span class="n">y</span><span class="p">,</span> <span class="n">sourceVC</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="n">bounds</span><span class="o">.</span><span class="n">height</span> <span class="o">-</span> <span class="n">expandingView</span><span class="o">.</span><span class="n">frame</span><span class="o">.</span><span class="n">origin</span><span class="o">.</span><span class="n">y</span><span class="p">]</span> <span class="k">let</span> <span class="nv">minExpandingViewDim</span> <span class="o">=</span> <span class="nf">min</span><span class="p">(</span><span class="n">expandableView</span><span class="o">.</span><span class="n">bounds</span><span class="o">.</span><span class="n">width</span><span class="p">,</span> <span class="n">expandableView</span><span class="o">.</span><span class="n">bounds</span><span class="o">.</span><span class="n">height</span><span class="p">)</span> <span class="k">let</span> <span class="nv">maxOffsetVal</span> <span class="o">=</span> <span class="n">offsets</span><span class="o">.</span><span class="nf">maxElement</span><span class="p">()</span><span class="o">!</span> <span class="k">let</span> <span class="nv">maxSourceDim</span> <span class="o">=</span> <span class="nf">max</span><span class="p">(</span><span class="n">sourceVC</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="n">bounds</span><span class="o">.</span><span class="n">width</span><span class="p">,</span> <span class="n">sourceVC</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="n">bounds</span><span class="o">.</span><span class="n">height</span><span class="p">)</span> <span class="c1">// to make sure that source is filled by `expandingView`</span> <span class="c1">// especially if the view has rounded edges</span> <span class="k">let</span> <span class="nv">threshold_scale</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="o">=</span> <span class="mi">2</span> <span class="k">let</span> <span class="nv">scale</span> <span class="o">=</span> <span class="p">(</span><span class="n">maxSourceDim</span> <span class="o">+</span> <span class="n">maxOffsetVal</span><span class="p">)</span> <span class="o">/</span> <span class="n">minExpandingViewDim</span> <span class="o">+</span> <span class="n">threshold_scale</span> <span class="k">return</span> <span class="kt">CGAffineTransformMakeScale</span><span class="p">(</span><span class="n">scale</span><span class="p">,</span> <span class="n">scale</span><span class="p">)</span> <span class="p">}</span></code></pre></figure> <p>Here is how the transition code look like.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">func</span> <span class="nf">animateTransition</span><span class="p">(</span><span class="nv">transitionContext</span><span class="p">:</span> <span class="kt">UIViewControllerContextTransitioning</span><span class="p">)</span> <span class="p">{</span> <span class="cm">/** Transitions involves: - adding source and destination view controller to the container view - transforming expandable view so it fills entire screen - finally displaying the destination view controller - bringing back previous state of expandable view */</span> <span class="k">let</span> <span class="nv">sourceVC</span> <span class="o">=</span> <span class="n">transitionContext</span><span class="o">.</span><span class="nf">viewControllerForKey</span><span class="p">(</span><span class="kt">UITransitionContextFromViewControllerKey</span><span class="p">)</span><span class="o">!</span> <span class="k">let</span> <span class="nv">destinationVC</span> <span class="o">=</span> <span class="n">transitionContext</span><span class="o">.</span><span class="nf">viewControllerForKey</span><span class="p">(</span><span class="kt">UITransitionContextToViewControllerKey</span><span class="p">)</span><span class="o">!</span> <span class="n">destinationVC</span><span class="o">.</span><span class="n">modalPresentationStyle</span> <span class="o">=</span> <span class="o">.</span><span class="kt">Custom</span> <span class="n">destinationVC</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="n">alpha</span> <span class="o">=</span> <span class="mi">0</span> <span class="k">let</span> <span class="nv">containerView</span> <span class="o">=</span> <span class="n">transitionContext</span><span class="o">.</span><span class="nf">containerView</span><span class="p">()</span><span class="o">!</span> <span class="n">containerView</span><span class="o">.</span><span class="nf">addSubview</span><span class="p">(</span><span class="n">sourceVC</span><span class="o">.</span><span class="n">view</span><span class="p">)</span> <span class="n">containerView</span><span class="o">.</span><span class="nf">addSubview</span><span class="p">(</span><span class="n">destinationVC</span><span class="o">.</span><span class="n">view</span><span class="p">)</span> <span class="k">let</span> <span class="nv">beginZPosition</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="n">expandableView</span><span class="o">.</span><span class="n">layer</span><span class="o">.</span><span class="n">zPosition</span> <span class="k">let</span> <span class="nv">beginTransform</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="n">expandableView</span><span class="o">.</span><span class="n">transform</span> <span class="k">let</span> <span class="nv">finalTransform</span> <span class="o">=</span> <span class="nf">calculateFinalTransformOfExpandingViewInSourceVC</span><span class="p">(</span><span class="k">self</span><span class="o">.</span><span class="n">expandableView</span><span class="p">,</span> <span class="nv">sourceVC</span><span class="p">:</span> <span class="n">sourceVC</span><span class="p">)</span> <span class="k">self</span><span class="o">.</span><span class="n">expandableView</span><span class="o">.</span><span class="n">layer</span><span class="o">.</span><span class="n">zPosition</span> <span class="o">=</span> <span class="kt">CGFloat</span><span class="o">.</span><span class="n">max</span> <span class="o">-</span> <span class="mi">1</span> <span class="kt">UIView</span><span class="o">.</span><span class="nf">animateWithDuration</span><span class="p">(</span><span class="k">self</span><span class="o">.</span><span class="n">expandViewAnimationDuration</span><span class="p">,</span> <span class="nv">delay</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">options</span><span class="p">:</span> <span class="o">.</span><span class="kt">CurveEaseIn</span><span class="p">,</span> <span class="nv">animations</span><span class="p">:</span> <span class="p">{</span> <span class="k">self</span><span class="o">.</span><span class="n">expandableView</span><span class="o">.</span><span class="n">transform</span> <span class="o">=</span> <span class="n">finalTransform</span> <span class="p">},</span> <span class="nv">completion</span><span class="p">:</span> <span class="p">{</span> <span class="n">_</span> <span class="k">in</span> <span class="kt">UIView</span><span class="o">.</span><span class="nf">animateWithDuration</span><span class="p">(</span><span class="k">self</span><span class="o">.</span><span class="n">presentVCAnimationDuration</span><span class="p">,</span> <span class="nv">animations</span><span class="p">:</span> <span class="p">{</span> <span class="n">destinationVC</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="n">alpha</span> <span class="o">=</span> <span class="mi">1</span> <span class="p">},</span> <span class="nv">completion</span><span class="p">:</span> <span class="p">{</span> <span class="n">_</span> <span class="k">in</span> <span class="k">self</span><span class="o">.</span><span class="n">expandableView</span><span class="o">.</span><span class="n">transform</span> <span class="o">=</span> <span class="n">beginTransform</span> <span class="k">self</span><span class="o">.</span><span class="n">expandableView</span><span class="o">.</span><span class="n">layer</span><span class="o">.</span><span class="n">zPosition</span> <span class="o">=</span> <span class="n">beginZPosition</span> <span class="n">transitionContext</span><span class="o">.</span><span class="nf">completeTransition</span><span class="p">(</span><span class="kc">true</span><span class="p">)</span> <span class="p">})</span> <span class="p">})</span> <span class="p">}</span></code></pre></figure> <p>Using <em>UITransitionContextFromViewControllerKey</em> and <em>UITransitionContextToViewControllerKey</em> keys, we can obtain source and destination view controllers via <code class="highlighter-rouge">viewControllerForKey</code> method.</p> <p>Then as documentation says presentation style of destination view controller is set to custom - <code class="highlighter-rouge">destinationVC.modalPresentationStyle = .Custom</code>.</p> <p><em>Animator</em> provides us <code class="highlighter-rouge">containerView</code> via <code class="highlighter-rouge">transitionContext</code> - see <a href="https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewControllerContextTransitioning_protocol/">UIViewControllerContextTransitioning</a> for details. Source and destination view controllers are added to this container view.</p> <p>We have to store somewhere begin state of <code class="highlighter-rouge">zPosition</code> and <code class="highlighter-rouge">layer.transform</code> of <em>expandable view</em> so we can bring it back to the state before scaling.</p> <p>After this there is just simple animation of a <code class="highlighter-rouge">transform</code> of <em>expandable view</em> and after this finished, the destination view controller is presented.</p> <p>After all of this is done and only destination view controller is on screen we can bring previous appearance of the <em>expandable view</em>.</p> <p>When everything is done you have to mark that the transition completed.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="n">transitionContext</span><span class="o">.</span><span class="nf">completeTransition</span><span class="p">(</span><span class="kc">true</span><span class="p">)</span></code></pre></figure> <p><img src="/uploads/post-49/transition-2.gif" alt="transition-2" /> <img src="/uploads/post-49/transition-4.gif" alt="transition-4" /></p> <h3 id="how-to-use-it">How to use it?</h3> <p>Here is one way you can use it programatically:</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">@IBAction</span> <span class="kd">func</span> <span class="nf">doAnimate</span><span class="p">(</span><span class="nv">sender</span><span class="p">:</span> <span class="kt">UIView</span><span class="p">)</span> <span class="p">{</span> <span class="k">let</span> <span class="nv">transitionDelegate</span> <span class="o">=</span> <span class="kt">ExpandingViewTransition</span><span class="p">(</span><span class="nv">expandingView</span><span class="p">:</span> <span class="n">sender</span><span class="p">,</span> <span class="nv">expandViewAnimationDuration</span><span class="p">:</span> <span class="mf">0.4</span><span class="p">,</span> <span class="nv">presentVCAnimationDuration</span><span class="p">:</span> <span class="mf">0.1</span><span class="p">)</span> <span class="k">let</span> <span class="nv">storyboard</span> <span class="o">=</span> <span class="kt">UIStoryboard</span><span class="p">(</span><span class="nv">name</span><span class="p">:</span> <span class="s">"Main"</span><span class="p">,</span> <span class="nv">bundle</span><span class="p">:</span> <span class="kc">nil</span><span class="p">)</span> <span class="k">let</span> <span class="nv">vc</span> <span class="o">=</span> <span class="n">storyboard</span><span class="o">.</span><span class="nf">instantiateViewControllerWithIdentifier</span><span class="p">(</span><span class="s">"SecondViewController"</span><span class="p">)</span> <span class="n">vc</span><span class="o">.</span><span class="n">transitioningDelegate</span> <span class="o">=</span> <span class="n">transitionDelegate</span> <span class="k">self</span><span class="o">.</span><span class="nf">presentViewController</span><span class="p">(</span><span class="n">vc</span><span class="p">,</span> <span class="nv">animated</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nv">completion</span><span class="p">:</span> <span class="kc">nil</span><span class="p">)</span> <span class="p">}</span></code></pre></figure> Sat, 14 May 2016 00:00:00 +0200 http://szulctomasz.com/2016/05/14/ios-custom-view-controller-transtion-by-expanding-view.html http://szulctomasz.com/2016/05/14/ios-custom-view-controller-transtion-by-expanding-view.html ios, custom transition, view controller Redesigned blog and changed to Jekyll <p>I thought about this migration for a bit last few months, but never had a time to perform it. I observed what people are using, and wanted to decrease amount of money I spend for hosting and to make my life easier.</p> <p>After quick research and thinking what I need, I decided to go with <a href="https://github.com/jekyll/jekyll">Jekyll</a>.</p> <p><em>If you’re planning to use static site generator, read this post and maybe you’ll pick Jekyll too.</em></p> <h4 id="why-jekyll-you-ask">Why Jekyll you ask?</h4> <p>I know there are several static site generators like this one but I picked Jekyll because this is the only one official tool supported by Github. Other tools works with Github too, like <a href="https://github.com/middleman/middleman">Middleman</a>. I just… wanted to have really easy migration and do not spent more time than needed.</p> <ul> <li>Hosting page on Github will allow me to drop my current hosting.</li> <li>I don’t like Wordpress articles editor. I always wanted to use markdown and write articles in <a href="https://github.com/atom/atom">Atom</a> which is great app btw! I can recommend it to everyone - Dropped <em>Sublime Text</em>, <em>Mou</em> and <em>TextWrangler</em> for it. Now I can use markdown and this is really great, articles look clear during writing and easy to maintain.</li> <li>I can host blog on github, so I have source control management.</li> <li>I do not need a big solution like Wordpress - I want to keep my blog simple.</li> <li>I played a lot with Python last month, working on RESTful service and frontend using <a href="http://flask.pocoo.org">Flask</a>. It uses <a href="http://jinja2.readthedocs.org/en/latest/">Jinja2</a>, and Jekyll also uses Jinja2 templating system, which I like - It is easy to do frontend.</li> <li>It is easier for me to create new pages, like page with list of all articles on the blog, or… okay, I have only just this one for the moment :)</li> <li>Syntax highlighting is better than the Crayon on Wordpress.</li> <li>There is no html tags floating around in the article, e.g. inline or not inline syntax highlighting code, or links, or images - oh, I hate this!</li> </ul> <p>And there is more things that I don’t want to think about now.</p> <p><img src="/uploads/post-48/1.png" alt="atom-screenshot" /></p> <h4 id="migration-process">Migration process</h4> <p>It took me ~10-12 hours to move 47 articles with images and links, and to convert everything to markdown, and update a bit default Jekyll’s theme.</p> <p>After I moved everything manually I found there is a <a href="https://import.jekyllrb.com/docs/wordpressdotcom/">migration tool</a> that allow you move your wordpress data to Jekyll. My bad, but now I do know very well how to create posts on Jekyll ;)</p> <p>Jekyll is nicely documented so I found answers for probably every of my questions in <a href="https://jekyllrb.com/docs/home/">official documentation</a>.</p> <h4 id="migration-is-done">Migration is done</h4> <p>That’s it. Blog based on Jekyll is up and running. Good, now I just need to write posts on the blog.</p> Fri, 15 Apr 2016 00:00:00 +0200 http://szulctomasz.com/2016/04/15/redesigned-blog-and-changed-to-jekyll.html http://szulctomasz.com/2016/04/15/redesigned-blog-and-changed-to-jekyll.html general Small break in writing - job seeking <p>Hey everyone. Thanks for reading my blog. I see many people visit the blog and I am very happy to see that (~50,000 pageviews)!</p> <p>As you probably know from the header note of the blog, <strong>I am looking for a job</strong> for a while now, and will be looking for a next month or so, I guess.</p> <p>I’ll probably find some time to post a new blog posts, but I am mainly focused on finding new job.</p> <p>If you know any California (San Francisco Bay Area, Los Angeles)* companies that have own products and looking for an iOS engineer to help them make those products better, I’d love to hear about them!</p> <p>Wish me luck!</p> <p>*H1B visa sponsorship needed.</p> Fri, 26 Feb 2016 00:00:00 +0100 http://szulctomasz.com/2016/02/26/small-break-in-writing-job-seeking.html http://szulctomasz.com/2016/02/26/small-break-in-writing-job-seeking.html general, job iOS: Selecting audio inputs <p>Recently I learned how to select microphone inputs and switch between them. I think it is worth to share, so you might can find it useful. The case was to switch between built-in and external headset microphone.</p> <h3 id="getting-devices-for-media-type">Getting devices for media type</h3> <p>The system is returning just one audio device no matter whether headphones are connected or not.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">import</span> <span class="kt">AVFoundation</span> <span class="o">...</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">session</span><span class="p">:</span> <span class="kt">AVAudioSession</span><span class="o">!</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">inputs</span> <span class="o">=</span> <span class="p">[</span><span class="kt">AVAudioSessionPortDescription</span><span class="p">]()</span> <span class="o">...</span> <span class="c1">/// configure audio session</span> <span class="n">session</span> <span class="o">=</span> <span class="kt">AVAudioSession</span><span class="o">.</span><span class="nf">sharedInstance</span><span class="p">()</span> <span class="k">try!</span> <span class="n">session</span><span class="o">.</span><span class="nf">setCategory</span><span class="p">(</span><span class="kt">AVAudioSessionCategoryPlayAndRecord</span><span class="p">)</span> <span class="c1">/// get devices</span> <span class="k">let</span> <span class="nv">devices</span> <span class="o">=</span> <span class="kt">AVCaptureDevice</span><span class="o">.</span><span class="nf">devicesWithMediaType</span><span class="p">(</span><span class="kt">AVMediaTypeAudio</span><span class="p">)</span></code></pre></figure> <figure class="highlight"><pre><code class="language-plain" data-lang="plain">// for phone with just built-in microphone &lt;__NSArrayM 0x15e6a3d0&gt;( &lt;AVCaptureFigAudioDevice: 0x15e676f0 [iPhone Microphone][com.apple.avfoundation.avcapturedevice.built-in_audio:0]&gt; ) // for phone with headset connected &lt;__NSArrayM 0x1665c3d0&gt;( &lt;AVCaptureFigAudioDevice: 0x16663560 [Headphones][com.apple.avfoundation.avcapturedevice.built-in_audio:0]&gt; )</code></pre></figure> <p>Well, by asking for device with specific media type we’re not able to select proper audio input - system selects the source on its own.</p> <h3 id="ports">Ports</h3> <p>Instead of asking for devices with specific media type we can ask AVAudioSession to return all input ports, iterate through them and select the ones we need.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">if</span> <span class="k">let</span> <span class="nv">availableInputs</span> <span class="o">=</span> <span class="n">session</span><span class="o">.</span><span class="n">availableInputs</span> <span class="p">{</span> <span class="k">for</span> <span class="n">input</span> <span class="k">in</span> <span class="n">availableInputs</span> <span class="p">{</span> <span class="k">if</span> <span class="n">input</span><span class="o">.</span><span class="n">portType</span> <span class="o">==</span> <span class="kt">AVAudioSessionPortBuiltInMic</span> <span class="o">||</span> <span class="n">input</span><span class="o">.</span><span class="n">portType</span> <span class="o">==</span> <span class="kt">AVAudioSessionPortHeadsetMic</span> <span class="p">{</span> <span class="n">inputs</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="n">input</span><span class="p">)</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>To select an input, we need to set it as a preferred input.</p> <figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">private</span> <span class="kd">func</span> <span class="nf">selectInput</span><span class="p">(</span><span class="nv">input</span><span class="p">:</span> <span class="kt">AVAudioSessionPortDescription</span><span class="p">)</span> <span class="p">{</span> <span class="k">do</span> <span class="p">{</span> <span class="k">try</span> <span class="n">session</span><span class="o">.</span><span class="nf">setPreferredInput</span><span class="p">(</span><span class="n">input</span><span class="p">)</span> <span class="p">}</span> <span class="k">catch</span> <span class="k">let</span> <span class="nv">error</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="n">error</span><span class="p">)</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> Mon, 08 Feb 2016 00:00:00 +0100 http://szulctomasz.com/2016/02/08/ios-selecting-audio-inputs.html http://szulctomasz.com/2016/02/08/ios-selecting-audio-inputs.html ios, swift, avfoundation Xcode: BUILD FAILED while building project using xcodebuild and Xcode 7.2 <p>On Friday I worked on a framework and wanted to create script that would give me Debug or Release version of a framework, so it can run both on simulator and actual device. I managed to do this finally but it took me about a long time to realize that there is a bug in Xcode 7.2 that does not allow you to build project for simulator.</p> <p>Reported bug: <a href="http://www.openradar.me/23857648">rdar://23857648</a> (Copied below)</p> <figure class="highlight"><pre><code class="language-plain" data-lang="plain">Summary: xcodebuild fails to build a simple project on the command line. It seems there is a problem setting the platform variable. Some discussion here: https://forums.developer.apple.com/thread/27975 Steps to Reproduce: 1. Create a new iOS 'Single View Application' called 'TestBuildCmd' from the project template in Xcode 7.2 2. In a terminal cd to the directory containing the project file (TestBuildCmd.xcodeproj) 3. Execute the following command to build on the command line: xcodebuild -project TestBuildCmd.xcodeproj -scheme TestBuildCmd -sdk iphonesimulator clean build Expected Results: project builds and completes with "** BUILD SUCCEEDED **" output Actual Results: build fails. Lots of errors like this: /In file included from &lt;module-includes&gt;:1: /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator9.2.sdk/usr/include/sys/cdefs.h:707:2: error: Unsupported architecture #error Unsupported architecture /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator9.2.sdk/usr/include/sys/_types.h:55:9: error: unknown type name '__int64_t' typedef __int64_t __darwin_blkcnt_t; /* total blocks */ ^ ... Version: Xcode 7.2 (7C68) / iOS 9.2 SDK OS X 10.10.5</code></pre></figure> <p>To make it work you need to add argument to the xcodebuild command <code class="highlighter-rouge">-destination 'platform=iOS Simulator,name=iPhone 6,OS=latest'</code>.</p> Sun, 24 Jan 2016 00:00:00 +0100 http://szulctomasz.com/2016/01/24/xcode-build-failed-while-building-project-using-xcodebuild-and-xcode-7-2.html http://szulctomasz.com/2016/01/24/xcode-build-failed-while-building-project-using-xcodebuild-and-xcode-7-2.html xcode, bug, xcodebuild