<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Posts on Michal Konturek</title><link>http://michalkonturek.dev/posts/</link><description>Recent content in Posts on Michal Konturek</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><copyright>&lt;a href="https://creativecommons.org/licenses/by-nc/4.0/" target="_blank" rel="noopener"&gt;CC BY-NC 4.0&lt;/a&gt;</copyright><lastBuildDate>Sat, 07 Mar 2026 00:00:00 +0000</lastBuildDate><atom:link href="http://michalkonturek.dev/posts/index.xml" rel="self" type="application/rss+xml"/><item><title>Modernizing This Blog With Claude Code</title><link>http://michalkonturek.dev/posts/modernizing-this-blog-with-claude-code/</link><pubDate>Sat, 07 Mar 2026 00:00:00 +0000</pubDate><guid>http://michalkonturek.dev/posts/modernizing-this-blog-with-claude-code/</guid><description>&lt;p&gt;I recently sat down to modernize this blog — update the domain, fix a broken build, clean up years of accumulated tech debt. What would normally take an afternoon of digging through docs, I did conversationally with &lt;a href="https://claude.ai/code"&gt;Claude Code&lt;/a&gt; in a single session.&lt;/p&gt;
&lt;p&gt;Here is what we did.&lt;/p&gt;
&lt;h2 id="the-starting-point"&gt;The Starting Point&lt;/h2&gt;
&lt;p&gt;The site was running on Hugo with the &lt;code&gt;hello-friend-ng&lt;/code&gt; theme committed directly into the repository. It had not been touched in years. The Hugo version had moved on significantly, and the build was broken. There were also 10 open Dependabot pull requests and 75 reported vulnerabilities — all from an unused theme.&lt;/p&gt;</description><content type="html"><![CDATA[<p>I recently sat down to modernize this blog — update the domain, fix a broken build, clean up years of accumulated tech debt. What would normally take an afternoon of digging through docs, I did conversationally with <a href="https://claude.ai/code">Claude Code</a> in a single session.</p>
<p>Here is what we did.</p>
<h2 id="the-starting-point">The Starting Point</h2>
<p>The site was running on Hugo with the <code>hello-friend-ng</code> theme committed directly into the repository. It had not been touched in years. The Hugo version had moved on significantly, and the build was broken. There were also 10 open Dependabot pull requests and 75 reported vulnerabilities — all from an unused theme.</p>
<h2 id="fixing-a-broken-build">Fixing a Broken Build</h2>
<p>The first problem: <code>hugo --minify</code> failed with errors. Three deprecated APIs had been removed from Hugo since the theme was last updated:</p>
<ul>
<li><code>.Site.Language.Get</code> — removed from Hugo&rsquo;s language API</li>
<li><code>.Site.GoogleAnalytics</code> — moved to <code>site.Config.Services.GoogleAnalytics.ID</code></li>
<li><code>.Site.Author</code> — moved to <code>[params.Author]</code></li>
</ul>
<p>Claude diagnosed all three, created layout overrides in <code>layouts/</code> to fix them without touching the theme directly, and the build passed. This is exactly the kind of yak-shaving that normally eats an hour — reading changelogs, hunting down the right replacement API, checking if the fix actually works.</p>
<h2 id="cleaning-up-dependabot">Cleaning Up Dependabot</h2>
<p>The 75 vulnerabilities were all coming from <code>themes/hello-friend</code> — an old theme that was never used. The site used <code>hello-friend-ng</code>. The solution was simple: delete it.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>git rm -r themes/hello-friend
</span></span></code></pre></div><p>That dropped the vulnerability count immediately. Claude also closed all 10 stale Dependabot PRs in one go via the GitHub CLI.</p>
<h2 id="new-domain">New Domain</h2>
<p>I moved from <code>michal.codes</code> to <code>michalkonturek.dev</code>. This meant updating:</p>
<ul>
<li><code>baseurl</code> in <code>config.toml</code></li>
<li>The CNAME echo step in the GitHub Actions workflow</li>
<li><code>static/CNAME</code> (properly, so Hugo copies it to <code>public/</code> on every build)</li>
<li><code>content/about.md</code></li>
</ul>
<p>Claude caught all the references across the repo in one search.</p>
<h2 id="modernizing-the-config">Modernizing the Config</h2>
<p>A few housekeeping changes:</p>
<ul>
<li>Renamed <code>config.toml</code> to <code>hugo.toml</code> — Hugo&rsquo;s preferred filename since v0.110</li>
<li>Removed the deprecated <code>[author]</code> top-level section (now lives in <code>[params.Author]</code>)</li>
<li>Updated the site description to something less dated</li>
</ul>
<h2 id="switching-the-theme-to-a-hugo-module">Switching the Theme to a Hugo Module</h2>
<p>The biggest improvement. Instead of committing the entire theme into the repository, the theme is now a proper Hugo module:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-toml" data-lang="toml"><span style="display:flex;"><span>[<span style="color:#a6e22e">module</span>]
</span></span><span style="display:flex;"><span>  [[<span style="color:#a6e22e">module</span>.<span style="color:#a6e22e">imports</span>]]
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">path</span> = <span style="color:#e6db74">&#34;github.com/rhazdon/hugo-theme-hello-friend-ng&#34;</span>
</span></span></code></pre></div><p>With a <code>go.mod</code> and <code>go.sum</code> tracking the dependency. Updating the theme in the future is now just:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>hugo mod get -u <span style="color:#f92672">&amp;&amp;</span> hugo mod tidy
</span></span></code></pre></div><p>Switching to the module also pulled in a newer version of the theme that had already fixed the deprecated APIs — so the layout overrides we created earlier could be deleted. The repo ended up smaller and cleaner than when we started.</p>
<h2 id="fixing-the-deployment-pipeline">Fixing the Deployment Pipeline</h2>
<p>The old workflow (<code>gh.yml</code>) pushed the built site to a <code>master</code> branch, which GitHub Pages then served. GitHub now has a better approach: deploy directly from an Actions artifact without needing a separate branch.</p>
<p>Claude replaced the old workflow with the modern <code>actions/deploy-pages</code> approach, added Go setup for Hugo module support, deleted the now-redundant <code>master</code> branch, created <code>main</code> from <code>develop</code>, and fixed a GitHub Pages environment protection rule that was blocking deployment — all via the GitHub API.</p>
<h2 id="reflections">Reflections</h2>
<p>The thing that stood out was how Claude handled the layered nature of the problems. Each fix revealed the next issue, and it just kept going — diagnosing, fixing, verifying the build, moving on. No context switching, no tab soup of documentation.</p>
<p>It is not that any individual task was hard. It is that there were a lot of them, they were interconnected, and doing them manually would have meant a lot of stopping and starting. With Claude Code it felt more like pairing with someone who had already read all the changelogs.</p>
<p>The repository is now leaner, the build is clean, and the deployment pipeline is straightforward. A good afternoon&rsquo;s work.</p>
]]></content></item><item><title>Modernizing MKUnits With AI</title><link>http://michalkonturek.dev/posts/modernizing-mkunits-with-ai/</link><pubDate>Sat, 21 Feb 2026 00:00:00 +0000</pubDate><guid>http://michalkonturek.dev/posts/modernizing-mkunits-with-ai/</guid><description>&lt;p&gt;&lt;a href="https://github.com/michalkonturek/MKUnits"&gt;MKUnits&lt;/a&gt; is a unit conversion library for Swift that I originally wrote back in 2014. It had a good run — 342 stars, used by people building apps that needed to convert between mass, length, area, volume, and more. Then life happened, and the library sat untouched from 2016 until earlier this year.&lt;/p&gt;
&lt;p&gt;A decade is a long time in Swift. The language that existed in 2016 (Swift 3.0) is almost unrecognisable compared to Swift 6.0. I decided to bring MKUnits fully up to date, and I did it with AI assistance.&lt;/p&gt;</description><content type="html"><![CDATA[<p><a href="https://github.com/michalkonturek/MKUnits">MKUnits</a> is a unit conversion library for Swift that I originally wrote back in 2014. It had a good run — 342 stars, used by people building apps that needed to convert between mass, length, area, volume, and more. Then life happened, and the library sat untouched from 2016 until earlier this year.</p>
<p>A decade is a long time in Swift. The language that existed in 2016 (Swift 3.0) is almost unrecognisable compared to Swift 6.0. I decided to bring MKUnits fully up to date, and I did it with AI assistance.</p>
<p>Here is what that looked like.</p>
<h2 id="where-things-stood">Where Things Stood</h2>
<p>The last commit before this modernisation effort was from November 2016. The library was:</p>
<ul>
<li>Written in Swift 3.0</li>
<li>Distributed via CocoaPods and Carthage</li>
<li>Tested with XCTest</li>
<li>Structured as an Xcode-first project</li>
</ul>
<p>None of these are wrong per se, but none of them are where the Swift ecosystem is in 2026 either.</p>
<h2 id="swift-3-to-swift-6">Swift 3 to Swift 6</h2>
<p>The biggest jump. Swift 6 introduced strict concurrency checking — every type that crosses concurrency boundaries must conform to <code>Sendable</code>. The AI worked through the entire codebase, adding <code>Sendable</code> conformance where needed, replacing deprecated APIs, and updating Swift syntax that had evolved across ten major versions.</p>
<p>What makes this particularly tedious to do by hand is that you have to understand <em>why</em> each change is needed, not just <em>what</em> to change. The compiler error messages for Swift 6 concurrency are verbose. Having the AI reason through them systematically meant we got through the migration without getting lost in the noise.</p>
<p>The result was tagged as <strong>v5.0.0</strong>.</p>
<h2 id="spm-first">SPM First</h2>
<p>CocoaPods and Carthage were the standard in 2016. Swift Package Manager is the standard now. The project was restructured from the ground up as an SPM-first library — proper <code>Package.swift</code>, sensible target layout, no more <code>.xcodeproj</code> as the source of truth.</p>
<p>This also meant the library could now be used on Linux, not just macOS and iOS. A GitHub Actions workflow was added to run tests on both platforms on every push — something that simply was not possible with the old Xcode-only setup.</p>
<p>Tagged as <strong>v6.0.0</strong>.</p>
<h2 id="xctest-to-swift-testing">XCTest to Swift Testing</h2>
<p>Apple introduced <a href="https://developer.apple.com/xcode/swift-testing/">Swift Testing</a> as the modern replacement for XCTest. The entire test suite — hundreds of tests — was migrated to the new framework.</p>
<p>The difference is meaningful. Swift Testing uses macros (<code>#expect</code>, <code>#require</code>) instead of assertion functions, has better failure messages, and integrates more naturally with the rest of Swift. Migrating a large test suite by hand is exactly the kind of repetitive structural work that AI handles well. The tests went from this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">testThatItConverts_1_Kilogram_to_Pound</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">let</span> sut = MKMass.kilogram(<span style="color:#ae81ff">1</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">let</span> result = sut.convert(to: MKMassUnit.pound)
</span></span><span style="display:flex;"><span>    XCTAssertEqual(result, MKMass.pound(<span style="color:#ae81ff">2.20462</span>))
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>To this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span>@Test <span style="color:#66d9ef">func</span> <span style="color:#a6e22e">kilogram_to_pound</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">let</span> sut = MKMass.kilogram(<span style="color:#ae81ff">1</span>)
</span></span><span style="display:flex;"><span>    #expect(sut.convert(to: .pound) == MKMass.pound(<span style="color:#ae81ff">2.20462</span>))
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Cleaner, more expressive, and all without manually touching each of the hundreds of test cases.</p>
<h2 id="new-units">New Units</h2>
<p>With the foundation modernised, adding new units became straightforward. Two new unit types were added:</p>
<p><strong>ByteUnit</strong> — digital storage conversions: bit, byte, kilobyte, megabyte, gigabyte, terabyte, and the full IEC binary variants (kibibyte, mebibyte, gibibyte, tebibyte).</p>
<p><strong>TemperatureUnit</strong> — this one required some thought. Most unit conversions are multiplicative (metres to feet is just a constant factor). Temperature is different — Celsius, Fahrenheit, and Kelvin all have different zero points, so conversion requires both scaling and offset. The AI worked through the maths, implemented offset-based conversion correctly, and added a full test suite to verify it.</p>
<h2 id="a-demo-target">A Demo Target</h2>
<p>A small command-line executable was added to the package — <code>MKUnitsDemo</code> — that exercises the library and serves as a living example of how to use it. Useful for anyone evaluating the library, and useful as a quick sanity check during development.</p>
<h2 id="reflections">Reflections</h2>
<p>The thing that struck me most was the speed at which a decade of drift can be addressed. Not because AI writes perfect code — it does not — but because it removes the activation energy. Looking at a 10-year-old Swift 3 codebase and knowing you need to bring it to Swift 6 is daunting. Starting a conversation and just&hellip; doing it, step by step, is not.</p>
<p>The AI was particularly good at the mechanical parts: migrating syntax, applying consistent patterns across many files, translating XCTest to Swift Testing. It was less useful for decisions that required judgement — like how to structure the offset-based temperature conversion, or what the right API shape should be. Those conversations happened, but they needed me to push back and think carefully.</p>
<p>MKUnits is now a modern Swift 6 library. It took one session to cover what ten years had left behind.</p>
]]></content></item><item><title>Integrating Cocoapods with a Swift project</title><link>http://michalkonturek.dev/posts/integrating-cocoapods-with-a-swift-project/</link><pubDate>Wed, 04 Jun 2014 06:37:10 +0100</pubDate><guid>http://michalkonturek.dev/posts/integrating-cocoapods-with-a-swift-project/</guid><description>&lt;p&gt;As Apple introduced &lt;a href="https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/index.html"&gt;Swift&lt;/a&gt;, their new programming language, you probably wonder how easily you can integrate it with existing Objective-C libraries that are available via &lt;a href="http://cocoapods.org/"&gt;CocoaPods&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You begin with creating a &lt;code&gt;Podfile&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;pod &amp;#39;MKUnits&amp;#39;, &amp;#39;~&amp;gt; 2.0.0&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;and running &lt;code&gt;pod install&lt;/code&gt; to install pods in your project.&lt;/p&gt;
&lt;p&gt;Nice, so far so good. &lt;a href="http://cocoapods.org/"&gt;Cocoapods&lt;/a&gt; successfully integrated with &lt;a href="https://developer.apple.com/xcode/"&gt;Xcode 6&lt;/a&gt; project. All we need to do now is to import our pod header file into &lt;a href="https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/index.html"&gt;Swift&lt;/a&gt; project. How do you do that?&lt;/p&gt;</description><content type="html"><![CDATA[<p>As Apple introduced <a href="https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/index.html">Swift</a>, their new programming language, you probably wonder how easily you can integrate it with existing Objective-C libraries that are available via <a href="http://cocoapods.org/">CocoaPods</a>.</p>
<p>You begin with creating a <code>Podfile</code>:</p>
<pre tabindex="0"><code>pod &#39;MKUnits&#39;, &#39;~&gt; 2.0.0&#39;
</code></pre><p>and running <code>pod install</code> to install pods in your project.</p>
<p>Nice, so far so good. <a href="http://cocoapods.org/">Cocoapods</a> successfully integrated with <a href="https://developer.apple.com/xcode/">Xcode 6</a> project. All we need to do now is to import our pod header file into <a href="https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/index.html">Swift</a> project. How do you do that?</p>
<p>Definitely not by adding <code>#import &lt;MKUnits/MKUnits.h&gt;</code> to a <a href="https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/index.html">Swift</a> file as it would result in this error:</p>
<pre><code>Expected expression
Expected identifier in import declaration
</code></pre>
<p>To expose Objective-C files to <a href="https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/index.html">Swift</a> you must use <strong>Objective-C bridging header</strong>, as <a href="https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/buildingcocoaapps/MixandMatch.html">Mix and Match</a> section of <a href="https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/buildingcocoaapps/index.html">Using Swift with Cocoa and Objective-C</a> documentation explains.</p>
<p>To do so, you must create a new Objective-C header file, let&rsquo;s call it <code>Example-Bridging-Header.h</code>, and add it to the project. Then, you need to set <code>Objective-C Bridging Header</code> for your target:</p>
<p><img src="/images/post-xcode-bridge.png" alt="Xcode Bridging Header"></p>
<p>and finally add import statement to the <strong>bridge header</strong>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span>#<span style="color:#66d9ef">import</span> &lt;<span style="color:#a6e22e">MKUnits</span><span style="color:#f92672">/</span><span style="color:#a6e22e">MKUnits</span>.<span style="color:#a6e22e">h</span>&gt;
</span></span></code></pre></div><p>Voilà! Now you can use your pods in <a href="https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/index.html">Swift</a>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span><span style="color:#66d9ef">let</span> kilograms = NSNumber.mass_kilogram(<span style="color:#ae81ff">2</span>)()
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">let</span> pounds = NSNumber.mass_pound(<span style="color:#ae81ff">10</span>)()
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">let</span> result = kilograms.add(pounds)
</span></span><span style="display:flex;"><span>println(result)
</span></span></code></pre></div><p>You can also integrate imported Objective-C classes with <a href="https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/index.html">Swift</a> types. For example, by creating an extension for <code>Int</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span><span style="color:#66d9ef">extension</span> <span style="color:#a6e22e">Int</span> {
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">func</span> <span style="color:#a6e22e">mass_kilogram</span>() -&gt; MKQuantity {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> MKQuantity.mass_kilogramWithAmount(<span style="color:#66d9ef">self</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">func</span> <span style="color:#a6e22e">mass_pound</span>() -&gt; MKQuantity {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> MKQuantity.mass_poundWithAmount(<span style="color:#66d9ef">self</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>you can replace previous code with:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span><span style="color:#66d9ef">let</span> kilograms = <span style="color:#ae81ff">2.</span>mass_kilogram()
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">let</span> pounds = <span style="color:#ae81ff">10.</span>mass_pound()
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">let</span> result = kilograms.add(pounds)
</span></span><span style="display:flex;"><span>println(result)
</span></span></code></pre></div><h2 id="references">References</h2>
<ul>
<li><a href="https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/buildingcocoaapps/MixandMatch.html">Swift and Objective-C in the Same Project</a></li>
<li><a href="https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/index.html">The Swift Programming Language</a></li>
</ul>
]]></content></item><item><title>Implementing Unit conversion library</title><link>http://michalkonturek.dev/posts/implementing-unit-conversion-library/</link><pubDate>Mon, 01 Jul 2013 18:19:29 +0100</pubDate><guid>http://michalkonturek.dev/posts/implementing-unit-conversion-library/</guid><description>&lt;p&gt;Last week I decided to implement unit conversion library for Objective-C called &lt;a href="https://github.com/michalkonturek/MKUnits"&gt;MKUnits&lt;/a&gt;. I was fully aware that there were many open-sourced implementation already available, but unfortunately none of them fitted my needs.&lt;/p&gt;
&lt;p&gt;I required a library that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;is easily extendable&lt;/li&gt;
&lt;li&gt;has built-in rounding functionality&lt;/li&gt;
&lt;li&gt;is precise up to at least 10 decimal places&lt;/li&gt;
&lt;li&gt;allows manipulation between units of the same group, i.e. kg and pounds, without the need of conversion&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="quantity-pattern"&gt;Quantity Pattern&lt;/h3&gt;
&lt;p&gt;Before I started coding, I had a quick read through the chapter of &lt;a href="http://www.amazon.co.uk/Analysis-Patterns-Reusable-Object-Models/dp/0201895420"&gt;Analysis Patterns&lt;/a&gt; by &lt;a href="http://martinfowler.com/"&gt;Martin Fowler&lt;/a&gt;, which describes the &lt;strong&gt;Quantity Pattern&lt;/strong&gt;. This pattern is extremely useful when you want to represent dimensioned quantities.&lt;/p&gt;</description><content type="html"><![CDATA[<p>Last week I decided to implement unit conversion library for Objective-C called <a href="https://github.com/michalkonturek/MKUnits">MKUnits</a>. I was fully aware that there were many open-sourced implementation already available, but unfortunately none of them fitted my needs.</p>
<p>I required a library that:</p>
<ul>
<li>is easily extendable</li>
<li>has built-in rounding functionality</li>
<li>is precise up to at least 10 decimal places</li>
<li>allows manipulation between units of the same group, i.e. kg and pounds, without the need of conversion</li>
</ul>
<h3 id="quantity-pattern">Quantity Pattern</h3>
<p>Before I started coding, I had a quick read through the chapter of <a href="http://www.amazon.co.uk/Analysis-Patterns-Reusable-Object-Models/dp/0201895420">Analysis Patterns</a> by <a href="http://martinfowler.com/">Martin Fowler</a>, which describes the <strong>Quantity Pattern</strong>. This pattern is extremely useful when you want to represent dimensioned quantities.</p>
<p>The idea behind this pattern is straightforward; it is a class that represents both the <code>amount</code> and the <code>unit</code>, and allows arithmetic behaviour, e.g. addition and subtraction, and conversion to other quantities.</p>
<p>The initial interface for the <code>MKQuantity</code> was:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span>@<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">MKUnit</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@interface MKQuantity : NSObject
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@property (nonatomic, copy) NSDecimalNumber <span style="color:#f92672">*</span>amount;
</span></span><span style="display:flex;"><span>@property (nonatomic, strong) MKUnit <span style="color:#f92672">*</span>unit;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">-</span> (id)initWithAmount:(NSNumber <span style="color:#f92672">*</span>)amount withUnit:(MKUnit <span style="color:#f92672">*</span>)unit;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">-</span> (instancetype)add:(MKQuantity <span style="color:#f92672">*</span>)other;
</span></span><span style="display:flex;"><span><span style="color:#f92672">-</span> (instancetype)subtract:(MKQuantity <span style="color:#f92672">*</span>)other;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">-</span> (instancetype)convertTo:(MKUnit <span style="color:#f92672">*</span>)unit;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">-</span> (NSComparisonResult)compare:(MKQuantity <span style="color:#f92672">*</span>)other;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@end
</span></span></code></pre></div><p>Also, it&rsquo;s worth to mention that <code>MKQuantity</code> is a <strong>value object</strong>.</p>
<blockquote>
<p>A <strong>value object</strong> is a simple object whose equality is not based on identity.</p>
</blockquote>
<p>In other words, two value objects are equal if all their attributes are equal. In this case, it is <code>amount</code> and <code>unit</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span><span style="color:#f92672">-</span> (BOOL)isEqual:(id)object {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span>[object isKindOfClass:[<span style="color:#66d9ef">self</span> <span style="color:#66d9ef">class</span>]]) <span style="color:#66d9ef">return</span> NO;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span>[[object unit] isEqual:<span style="color:#66d9ef">self</span>.unit]) <span style="color:#66d9ef">return</span> NO;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> [<span style="color:#66d9ef">self</span>.amount isEqual:[object amount]];
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">-</span> (NSUInteger)hash {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> [[NSString stringWithFormat:@<span style="color:#e6db74">&#34;%@%@%@&#34;</span>,
</span></span><span style="display:flex;"><span>             [<span style="color:#66d9ef">self</span> <span style="color:#66d9ef">class</span>], <span style="color:#66d9ef">self</span>.unit.symbol, <span style="color:#66d9ef">self</span>.amount] hash];
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><code>MKQuantity</code> is associated with <code>MKUnit</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span>@interface MKUnit : NSObject&lt;NSCopying&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@property (nonatomic, strong) NSString <span style="color:#f92672">*</span>name;
</span></span><span style="display:flex;"><span>@property (nonatomic, strong) NSString <span style="color:#f92672">*</span>symbol;
</span></span><span style="display:flex;"><span>@property (nonatomic, strong) NSDecimalNumber <span style="color:#f92672">*</span>ratio;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">-</span> (id)initWithName:(NSString <span style="color:#f92672">*</span>)name
</span></span><span style="display:flex;"><span>        withSymbol:(NSString <span style="color:#f92672">*</span>)symbol
</span></span><span style="display:flex;"><span>        withRatio:(NSDecimalNumber <span style="color:#f92672">*</span>)ratio;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>@end
</span></span></code></pre></div><p>The <code>ratio</code> property is used to convert unit to base unit, eg. for Length a base unit is <code>meter</code>, therefore, the ratio for <code>kilometer</code> unit is 1000 as <code>amount_in_meters = 1000 * amount_in_kilometers</code>.</p>
<h3 id="unit-conversion">Unit conversion</h3>
<p>Since this is a unit conversion library, the first type of a behaviour to be added is conversion.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span><span style="color:#f92672">-</span> (instancetype)convertTo:(MKUnit <span style="color:#f92672">*</span>)unit {
</span></span><span style="display:flex;"><span>    [<span style="color:#66d9ef">self</span> _assert_that_is_convertible_with_unit:unit];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    id converted = [<span style="color:#66d9ef">self</span>.unit convertAmount:<span style="color:#66d9ef">self</span>.amount to:unit];
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> [[<span style="color:#66d9ef">self</span> <span style="color:#66d9ef">class</span>] createWithAmount:converted withUnit:unit];
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The conversion process of unit A to unit B consists of:</p>
<ul>
<li>asserting that unit A is convertible with unit B</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span><span style="color:#f92672">-</span> (void)_assert_that_is_convertible_with_unit:(MKUnit <span style="color:#f92672">*</span>)unit {
</span></span><span style="display:flex;"><span>    NSAssert(
</span></span><span style="display:flex;"><span>      [<span style="color:#66d9ef">self</span>.unit isConvertibleWith:unit],
</span></span><span style="display:flex;"><span>      UNITS_NOT_CONVERTIBLE
</span></span><span style="display:flex;"><span>      );
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The aim is to perform conversion and arithmetic operations not only on the same units, but also on the other units of the same quantity, eg. adding 500 pounds to 2.5 kilograms should be allowed, but adding 500 grams to 1 meter should not.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span><span style="color:#f92672">-</span> (BOOL)isConvertibleWith:(MKUnit <span style="color:#f92672">*</span>)unit {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> [unit isMemberOfClass:[<span style="color:#66d9ef">self</span> <span style="color:#66d9ef">class</span>]];
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><ul>
<li>converting <code>amount</code> of unit A to <code>amount</code> of unit B</li>
</ul>
<p>Firstly the <code>amount</code> of unit A must be converted to <code>amount</code> of base unit, and then the result is converted to <code>amount</code> of unit B.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span><span style="color:#f92672">-</span> (NSNumber <span style="color:#f92672">*</span>)convertAmount:(NSNumber <span style="color:#f92672">*</span>)amount to:(MKUnit <span style="color:#f92672">*</span>)unit {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> [<span style="color:#66d9ef">self</span> convertAmount:amount from:<span style="color:#66d9ef">self</span> to:unit];
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">-</span> (NSNumber <span style="color:#f92672">*</span>)convertAmount:(NSNumber <span style="color:#f92672">*</span>)amount
</span></span><span style="display:flex;"><span>                       from:(MKUnit <span style="color:#f92672">*</span>)from to:(MKUnit <span style="color:#f92672">*</span>)to {
</span></span><span style="display:flex;"><span>    NSAssert([from isConvertibleWith:to], UNITS_NOT_CONVERTIBLE);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    id baseAmount = [from convertToBaseUnit:amount];
</span></span><span style="display:flex;"><span>    id converted = [to convertFromBaseUnit:baseAmount];
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> converted;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><ul>
<li>creating new <code>Quantity</code> object of unit B with converted <code>amount</code></li>
</ul>
<p>This is due general consensus that a <code>Value Object</code> should be entirely immutable.</p>
<h3 id="arithmetic">Arithmetic</h3>
<p>Addition and subtraction operations of two quantities are allowed as long as their units are convertible.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span><span style="color:#f92672">-</span> (instancetype)add:(MKQuantity <span style="color:#f92672">*</span>)other {
</span></span><span style="display:flex;"><span>    MKQuantity <span style="color:#f92672">*</span>converted = [other convertTo:<span style="color:#66d9ef">self</span>.unit];
</span></span><span style="display:flex;"><span>    id amount = [<span style="color:#66d9ef">self</span>.amount decimalNumberByAdding:converted.amount];
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> [[<span style="color:#66d9ef">self</span> <span style="color:#66d9ef">class</span>] createWithAmount:amount withUnit:<span style="color:#66d9ef">self</span>.unit];
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">-</span> (instancetype)subtract:(MKQuantity <span style="color:#f92672">*</span>)other {
</span></span><span style="display:flex;"><span>    MKQuantity <span style="color:#f92672">*</span>converted = [other convertTo:<span style="color:#66d9ef">self</span>.unit];
</span></span><span style="display:flex;"><span>    id amount = [<span style="color:#66d9ef">self</span>.amount decimalNumberBySubtracting:converted.amount];
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> [[<span style="color:#66d9ef">self</span> <span style="color:#66d9ef">class</span>] createWithAmount:amount withUnit:<span style="color:#66d9ef">self</span>.unit];
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><strong>NB</strong> As this is a unit conversion library, we should only allow multiplication and division by scalar numbers.</p>
<p>If you would like to know more, please visit <a href="https://github.com/michalkonturek/MKUnits">MKUnits GitHub repository</a>.</p>
<p>I am <a href="http://twitter.com/michalkonturek">@michalkonturek</a> on Twitter.</p>
<p>Looking forward to your feedback.</p>
]]></content></item></channel></rss>