unpredictable.tech 2021-09-16T13:30:34+00:00 https://unpredictable.tech/ Victor Pavlychko Private APIs, Objective-C runtime, and Swift 2020-09-08T00:00:00+00:00 https://unpredictable.tech/2020/09/08/private-apis-objective-c-runtime-and-swift/ <p>Sometimes when building an app, we find ourselves in a situation when we want to use a private API. It may provide some important functionality that is not exposed (yet), or we may want to work around a known platform issue. Or, you may be just debugging your code and poking around to get extra details.</p> <!-- more --> <blockquote> <p>Whatever you are trying to achieve, keep in mind that private APIs are fragile and can change without notice, leaving you with a broken product and frustrated users.</p> </blockquote> <p>Swift adds another dimension here: restricted APIs marked as <a href="https://developer.apple.com/documentation/swift/objective-c_and_c_code_customization/making_objective-c_apis_unavailable_in_swift"><code class="language-plaintext highlighter-rouge">NS_SWIFT_UNAVAILABLE</code></a>. Those are not technically private, but they are considered not safe enough and thus are not exposed in Swift. Calling into such unavailable APIs is pretty ok as they are fully documented and accessible from Objective-C code. This post will focus on accessing a group of Swift-unavailable APIs related to dynamic message dispatch provided by <a href="https://developer.apple.com/documentation/foundation/nsmethodsignature"><code class="language-plaintext highlighter-rouge">NSMethodSignature</code></a>, <a href="https://developer.apple.com/documentation/foundation/nsinvocation"><code class="language-plaintext highlighter-rouge">NSInvocation</code></a>, and <a href="https://developer.apple.com/documentation/objectivec/nsobject/1571960-methodsignatureforselector?language=objc"><code class="language-plaintext highlighter-rouge">NSObject</code></a> classes. I encourage you to check the documentation if you are not familiar with those.</p> <p>Nothing is private as long as Objective-C runtime is involved. Things have changed slightly with the introduction of direct methods. Still, a lot of APIs remain accessible if you know how to invoke them.</p> <h2 id="invoking-private-apis-from-swift">Invoking private APIs from Swift</h2> <p>But how do you call a method that is not exposed by the SDK? Back in the Objective-C days, everything was easy — thanks to the language’s dynamic nature, you could declare a category with any private methods. They would automatically resolve at runtime.</p> <p>Swift is a type-safe language, and you no longer allowed to do that. Common approaches rely on Swift-compatible Foundation APIs like <a href="https://developer.apple.com/documentation/objectivec/nsobjectprotocol/1418867-perform"><code class="language-plaintext highlighter-rouge">perform(_:)</code></a> for simple functions or low-level <a href="https://developer.apple.com/documentation/objectivec/1418551-method_getimplementation"><code class="language-plaintext highlighter-rouge">method_getImplementation</code></a> and/or <a href="https://developer.apple.com/documentation/objectivec/1456712-objc_msgsend"><code class="language-plaintext highlighter-rouge">objc_msgSend</code></a> when dealing with more sophisticated signatures. Unfortunately, this results in complicated, verbose, and error-prone code.</p> <p>Can we do something better? While the original category-based approach is not available in Swift, we can try something similar — we are still calling into Objective-C.</p> <h3 id="a-cleaner-approach">A cleaner approach</h3> <p>The idea is analogous to the category trick we did previously: we can define an <code class="language-plaintext highlighter-rouge">@objc</code> protocol containing methods of interest, use the runtime to add conformance to the class in question retroactively, and then ask Swift to do the typecast. Thanks to the dynamic dispatch used in everything coming from the Objective-C world, the protocol will be implemented automatically by existing class methods we are looking for.</p> <p>To get started, we will define a protocol for <code class="language-plaintext highlighter-rouge">NSMethodSignature</code> matching what we can see in the Objective-C documentation. Notice the <code class="language-plaintext highlighter-rouge">@objc(getArgumentTypeAtIndex:)</code> annotation I used here. Normally, the compiler will generate the appropriate selector based on the method name, yet we may want to alter the auto-generated name. Using correct selector names is crucial in our case, where we have to match underlying API signatures perfectly.</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@objc</span> <span class="kd">protocol</span> <span class="kt">NSMethodSignaturePrivate</span> <span class="p">{</span> <span class="kd">static</span> <span class="kd">func</span> <span class="nf">signature</span><span class="p">(</span><span class="nv">objCTypes</span><span class="p">:</span> <span class="kt">UnsafePointer</span><span class="o">&lt;</span><span class="kt">CChar</span><span class="o">&gt;</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">NSMethodSignaturePrivate</span><span class="p">?</span> <span class="k">var</span> <span class="nv">numberOfArguments</span><span class="p">:</span> <span class="kt">Int</span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span> <span class="kd">@objc</span><span class="p">(</span><span class="nv">getArgumentTypeAtIndex</span><span class="p">:)</span> <span class="kd">func</span> <span class="nf">getArgumentType</span><span class="p">(</span><span class="n">at</span> <span class="nv">index</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">UnsafePointer</span><span class="o">&lt;</span><span class="kt">CChar</span><span class="o">&gt;</span> <span class="k">var</span> <span class="nv">frameLength</span><span class="p">:</span> <span class="kt">Int</span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span> <span class="k">var</span> <span class="nv">isOneway</span><span class="p">:</span> <span class="kt">ObjCBool</span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span> <span class="k">var</span> <span class="nv">methodReturnType</span><span class="p">:</span> <span class="kt">UnsafePointer</span><span class="o">&lt;</span><span class="kt">CChar</span><span class="o">&gt;</span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span> <span class="k">var</span> <span class="nv">methodReturnLength</span><span class="p">:</span> <span class="kt">Int</span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>Once we have that protocol, we can attach it with explicit objc runtime calls:</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Obtain class reference from runtime:</span> <span class="kd">let</span> <span class="p">`</span><span class="nv">class</span><span class="p">`</span> <span class="o">=</span> <span class="s">"NSMethodSignature"</span><span class="o">.</span><span class="n">withCString</span> <span class="p">{</span> <span class="k">return</span> <span class="nf">objc_getClass</span><span class="p">(</span><span class="nv">$0</span><span class="p">)</span> <span class="k">as!</span> <span class="kt">AnyClass</span> <span class="p">}</span> <span class="c1">// Add protocol conformance:</span> <span class="nf">class_addProtocol</span><span class="p">(`</span><span class="nv">class</span><span class="p">`,</span> <span class="kt">NSMethodSignaturePrivate</span><span class="o">.</span><span class="k">self</span><span class="p">)</span> <span class="c1">// Get `NSMethodSignaturePrivate.Type` meta-type reference</span> <span class="k">let</span> <span class="nv">NSMethodSignatureClass</span> <span class="o">=</span> <span class="p">`</span><span class="nv">class</span><span class="p">`</span> <span class="k">as!</span> <span class="kt">NSMethodSignaturePrivate</span><span class="o">.</span><span class="k">Type</span> </code></pre></div></div> <p>Whoa, that’s some boilerplate, and we haven’t added any error checks yet — looks like the right candidate for a helper function. We will fix that right after testing that everything works as expected.</p> <p>We will be working with method signatures here, and if you are not familiar with those, I recommend reading <a href="https://nshipster.com/type-encodings/">an excellent article from NSHipster</a> to learn more about type encoding in Objective-C. As a quick recap, I’ll remind that <code class="language-plaintext highlighter-rouge">v@:</code> signature stays for “a method that returns void and accepts two implicit parameters: instance reference and method selector.”</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Call private class function:</span> <span class="k">let</span> <span class="nv">signature</span> <span class="o">=</span> <span class="s">"v@:"</span><span class="o">.</span><span class="n">withCString</span> <span class="p">{</span> <span class="k">return</span> <span class="kt">NSMethodSignatureClass</span><span class="o">.</span><span class="nf">signature</span><span class="p">(</span><span class="nv">objCTypes</span><span class="p">:</span> <span class="nv">$0</span><span class="p">)</span> <span class="p">}</span> <span class="c1">// Call private instance function:</span> <span class="nf">print</span><span class="p">(</span><span class="s">"Number of arguments: </span><span class="se">\(</span><span class="n">signature</span><span class="o">!.</span><span class="n">numberOfArguments</span><span class="se">)</span><span class="s">"</span><span class="p">)</span> </code></pre></div></div> <p>As intended, this says, “Number of arguments: 2,” which means that we have successfully constructed a signature for a method accepting two parameters.</p> <h3 id="reducing-boilerplate">Reducing boilerplate</h3> <p>Now back to the class import code. At first, it seems like we can leverage generics in a helper function to remove protocol reference duplication like this:</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="n">importClass</span><span class="o">&lt;</span><span class="kt">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">_</span> <span class="nv">className</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="k">as</span> <span class="nv">protocol</span><span class="p">:</span> <span class="kt">T</span><span class="o">.</span><span class="k">Type</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">T</span><span class="o">.</span><span class="k">Type</span> <span class="p">{</span> <span class="nf">fatalError</span><span class="p">(</span><span class="s">"Not implemented yet."</span><span class="p">)</span> <span class="p">}</span> <span class="k">let</span> <span class="nv">NSMethodSignatureClass</span> <span class="o">=</span> <span class="nf">importClass</span><span class="p">(</span><span class="s">"NSMethodSignature"</span><span class="p">,</span> <span class="nv">as</span><span class="p">:</span> <span class="kt">NSMethodSignaturePrivate</span><span class="o">.</span><span class="k">self</span><span class="p">)</span> <span class="k">let</span> <span class="nv">signature</span> <span class="o">=</span> <span class="s">"v@:"</span><span class="o">.</span><span class="nf">withCString</span><span class="p">(</span><span class="kt">NSMethodSignatureClass</span><span class="o">.</span><span class="nf">signature</span><span class="p">(</span><span class="nv">objCTypes</span><span class="p">:))</span> </code></pre></div></div> <p>Except this fails to compile, producing an error: “Static member <code class="language-plaintext highlighter-rouge">signature(objCTypes:)</code> cannot be used on protocol metatype <code class="language-plaintext highlighter-rouge">NSMethodSignaturePrivate.Protocol</code>.”</p> <p>You see, <code class="language-plaintext highlighter-rouge">NSMethodSignaturePrivate</code> is a protocol, and thus we need a concrete conforming type to create an <code class="language-plaintext highlighter-rouge">NSMethodSignaturePrivate.Type</code> value. Because of that, the <code class="language-plaintext highlighter-rouge">NSMethodSignaturePrivate.self</code> syntax was repurposed to produce an <code class="language-plaintext highlighter-rouge">NSMethodSignaturePrivate.Protocol</code>, which we can use with runtime functions. But that thing does not allow us to call class functions like <code class="language-plaintext highlighter-rouge">signature(objCTypes:)</code>.</p> <p>Let’s give it another try:</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="n">importClass</span><span class="o">&lt;</span><span class="kt">ProtocolType</span><span class="o">&gt;</span><span class="p">(</span><span class="n">_</span> <span class="nv">className</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">ProtocolType</span> <span class="p">{</span> <span class="nf">fatalError</span><span class="p">(</span><span class="s">"Not implemented yet."</span><span class="p">)</span> <span class="p">}</span> <span class="k">let</span> <span class="nv">NSMethodSignatureClass</span> <span class="o">=</span> <span class="nf">importClass</span><span class="p">(</span><span class="s">"NSMethodSignature"</span><span class="p">)</span> <span class="k">as</span> <span class="kt">NSMethodSignaturePrivate</span><span class="o">.</span><span class="k">Type</span> <span class="k">let</span> <span class="nv">signature</span> <span class="o">=</span> <span class="s">"v@:"</span><span class="o">.</span><span class="nf">withCString</span><span class="p">(</span><span class="kt">NSMethodSignatureClass</span><span class="o">.</span><span class="nf">signature</span><span class="p">(</span><span class="nv">objCTypes</span><span class="p">:))</span> </code></pre></div></div> <p>Now, this looks better, but how do we get <code class="language-plaintext highlighter-rouge">NSMethodSignaturePrivate.Protocol</code> from <code class="language-plaintext highlighter-rouge">NSMethodSignaturePrivate.Type</code>? I haven’t found any clear way to convert between those two, but we can use the type name as a middle ground here — use Swift reflection API to get the protocol name and then find it’s runtime counterpart with <code class="language-plaintext highlighter-rouge">objc_getProtocol</code>:</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="n">importClass</span><span class="o">&lt;</span><span class="kt">ProtocolType</span><span class="o">&gt;</span><span class="p">(</span><span class="n">_</span> <span class="nv">className</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">ProtocolType</span> <span class="p">{</span> <span class="k">let</span> <span class="nv">typeNameSuffix</span> <span class="o">=</span> <span class="s">".Type"</span> <span class="k">let</span> <span class="nv">protocolTypeName</span> <span class="o">=</span> <span class="kt">String</span><span class="p">(</span><span class="nv">reflecting</span><span class="p">:</span> <span class="kt">ProtocolType</span><span class="o">.</span><span class="k">self</span><span class="p">)</span> <span class="k">guard</span> <span class="n">protocolTypeName</span><span class="o">.</span><span class="nf">hasSuffix</span><span class="p">(</span><span class="n">typeNameSuffix</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span> <span class="nf">preconditionFailure</span><span class="p">(</span><span class="s">"Type `</span><span class="se">\(</span><span class="n">protocolTypeName</span><span class="se">)</span><span class="s">` is not a protocol type."</span><span class="p">)</span> <span class="p">}</span> <span class="k">let</span> <span class="nv">protocolName</span> <span class="o">=</span> <span class="n">protocolTypeName</span><span class="o">.</span><span class="nf">dropLast</span><span class="p">(</span><span class="n">typeNameSuffix</span><span class="o">.</span><span class="n">count</span><span class="p">)</span> <span class="k">guard</span> <span class="kd">let</span> <span class="p">`</span><span class="nv">protocol</span><span class="p">`</span> <span class="o">=</span> <span class="n">protocolName</span><span class="o">.</span><span class="nf">withCString</span><span class="p">(</span><span class="n">objc_getProtocol</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span> <span class="nf">preconditionFailure</span><span class="p">(</span><span class="s">"Type `</span><span class="se">\(</span><span class="n">protocolName</span><span class="se">)</span><span class="s">` is not an objc protocol."</span><span class="p">)</span> <span class="p">}</span> <span class="k">guard</span> <span class="kd">let</span> <span class="p">`</span><span class="nv">class</span><span class="p">`</span> <span class="o">=</span> <span class="n">className</span><span class="o">.</span><span class="nf">withCString</span><span class="p">(</span><span class="n">objc_getClass</span><span class="p">)</span> <span class="k">as?</span> <span class="kt">AnyClass</span> <span class="k">else</span> <span class="p">{</span> <span class="nf">preconditionFailure</span><span class="p">(</span><span class="s">"Class `</span><span class="se">\(</span><span class="n">className</span><span class="se">)</span><span class="s">` not found."</span><span class="p">)</span> <span class="p">}</span> <span class="k">if</span> <span class="o">!</span><span class="nf">class_addProtocol</span><span class="p">(`</span><span class="nv">class</span><span class="p">`,</span> <span class="p">`</span><span class="nv">protocol</span><span class="p">`)</span> <span class="p">{</span> <span class="nf">assertionFailure</span><span class="p">(</span><span class="s">"Failed to attach protocol `</span><span class="se">\(</span><span class="n">protocolName</span><span class="se">)</span><span class="s">` to class `</span><span class="se">\(</span><span class="n">className</span><span class="se">)</span><span class="s">`."</span><span class="p">)</span> <span class="p">}</span> <span class="k">guard</span> <span class="k">let</span> <span class="nv">result</span> <span class="o">=</span> <span class="p">`</span><span class="nv">class</span><span class="p">`</span> <span class="k">as?</span> <span class="kt">ProtocolType</span> <span class="k">else</span> <span class="p">{</span> <span class="nf">fatalError</span><span class="p">(</span><span class="s">"Failed to cast class `</span><span class="se">\(</span><span class="n">className</span><span class="se">)</span><span class="s">` to protocol `</span><span class="se">\(</span><span class="n">protocolName</span><span class="se">)</span><span class="s">`."</span><span class="p">)</span> <span class="p">}</span> <span class="k">return</span> <span class="n">result</span> <span class="p">}</span> </code></pre></div></div> <h2 id="conclusion">Conclusion</h2> <p>Objective-C runtime, together with Swift expressiveness, provides a lot of opportunities. We can access Swift-restricted or private APIs using a little hacking, just like we did in Objective-C (all safety measures are on us, though):</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">object</span> <span class="o">=</span> <span class="kt">NSDate</span><span class="p">()</span> <span class="k">let</span> <span class="nv">objectPrivate</span> <span class="o">=</span> <span class="n">object</span> <span class="k">as!</span> <span class="kt">NSObjectPrivate</span> <span class="k">let</span> <span class="nv">selector</span> <span class="o">=</span> <span class="kt">Selector</span><span class="p">(</span><span class="s">"description"</span><span class="p">)</span> <span class="k">let</span> <span class="nv">signature</span> <span class="o">=</span> <span class="n">objectPrivate</span><span class="o">.</span><span class="nf">methodSignature</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="n">selector</span><span class="p">)</span><span class="o">!</span> <span class="k">let</span> <span class="nv">invocation</span> <span class="o">=</span> <span class="kt">NSInvocationClass</span><span class="o">.</span><span class="nf">invocation</span><span class="p">(</span><span class="nv">methodSignature</span><span class="p">:</span> <span class="n">signature</span><span class="p">)</span> <span class="n">invocation</span><span class="o">.</span><span class="n">selector</span> <span class="o">=</span> <span class="n">selector</span> <span class="n">invocation</span><span class="o">.</span><span class="nf">invoke</span><span class="p">(</span><span class="nv">target</span><span class="p">:</span> <span class="n">object</span><span class="p">)</span> <span class="k">var</span> <span class="nv">unmanagedResult</span><span class="p">:</span> <span class="kt">Unmanaged</span><span class="o">&lt;</span><span class="kt">NSString</span><span class="o">&gt;</span><span class="p">?</span> <span class="o">=</span> <span class="kc">nil</span> <span class="n">invocation</span><span class="o">.</span><span class="nf">getReturnValue</span><span class="p">(</span><span class="o">&amp;</span><span class="n">unmanagedResult</span><span class="p">)</span> <span class="k">let</span> <span class="nv">result</span> <span class="o">=</span> <span class="n">unmanagedResult</span><span class="p">?</span><span class="o">.</span><span class="nf">takeRetainedValue</span><span class="p">()</span> <span class="nf">print</span><span class="p">(</span><span class="n">result</span> <span class="p">??</span> <span class="s">"&lt;nil&gt;"</span><span class="p">)</span> </code></pre></div></div> <p>Check this gist for a full example: <a href="https://gist.github.com/victor-pavlychko/8a896917d8c73f4dded594ab4782214e">https://gist.github.com/victor-pavlychko/8a896917d8c73f4dded594ab4782214e</a></p> Bringing the Best of SwiftUI to Our Team’s UIKit Code 2020-05-26T00:00:00+00:00 https://www.grammarly.com/blog/engineering/swiftui-uikit/ <p>Like pretty much all our colleagues in the field, iOS developers at Grammarly are excited about SwiftUI. Released at the 2019 Apple Worldwide Developers Conference (WWDC), it represents a major step forward in Apple’s support for building great user experiences. But as much as we want to use SwiftUI for everything, we can’t. For one thing, the libraries are still new and will probably take a few years to completely stabilize. Plus, SwiftUI is only bundled in iOS 13+, and we need to continue to support Grammarly for older versions of iOS. And finally, our existing UIKit code represents a huge, years-long investment for our team—we don’t want to just throw it out.</p> <p><a href="https://www.grammarly.com/blog/engineering/swiftui-uikit/">Read more&mldr;</a></p> String Index Type Safety in Swift 2019-12-19T00:00:00+00:00 https://www.grammarly.com/blog/engineering/string-index-type-safety-in-swift/ <p>Code is much like a conversation, and misunderstandings can happen when assumptions aren’t stated upfront. It brings to mind a quote (perhaps apocryphal) from the Enlightenment philosopher Voltaire: “If you wish to converse with me, first define your terms.” Though he is unlikely to have anticipated how this advice might apply to computer science, it rings true when considering the code development process. Misunderstandings lead to bugs—which our team found out when working with our iOS text manipulation logic.</p> <p><a href="https://www.grammarly.com/blog/engineering/string-index-type-safety-in-swift/">Read more&mldr;</a></p> DRY String Localization with Interface Builder 2017-08-21T00:00:00+00:00 https://unpredictable.tech/2017/08/21/dry-string-localization-with-interface-builder/ <p>Great applications should have great localization. And users will appreciate an option to use beloved apps in their native language. There is no excuse for developers not to support interface localization even on early stages of the development process, especially when it’s so easy to do.</p> <p>I prefer to design mostly with the Interface Builder. In this article I would like to share an approach I use in my projects to localize those resources.</p> <!-- more --> <p>Normally, when you try to localize a XIB file or storyboard, Xcode will happily clone the resource and you get stuck with duplicated view layouts. Yuck… That’s hardly a good option if you are trying to follow the DRY methodology 😕</p> <p>Instead of doing that I suggest filling “at”-prefixed localization terms in the Interface Builder and replacing them with the localized values in <code class="language-plaintext highlighter-rouge">viewDidLoad</code> or <code class="language-plaintext highlighter-rouge">awakeFromNib</code> methods of the corresponding objects. Here is an example of how it looks like in the Interface Builder:</p> <p>I use <code class="language-plaintext highlighter-rouge">@</code> prefix for localization terms to allow mixing them with any real values as needed. Additionally, leading <code class="language-plaintext highlighter-rouge">@@</code> sequence is replaced with a single <code class="language-plaintext highlighter-rouge">@</code> in case you need to specify unlocalized string starting with the “at” symbol.</p> <p>As it usually happens in Swift, we start with a protocol:</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">protocol</span> <span class="kt">Localizable</span> <span class="p">{</span> <span class="kd">func</span> <span class="nf">localize</span><span class="p">()</span> <span class="p">}</span> </code></pre></div></div> <p>Followed by an extension containing some helpers to localize strings and apply localized values to properties:</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kd">public</span> <span class="kd">extension</span> <span class="kt">Localizable</span> <span class="p">{</span> <span class="kd">public</span> <span class="kd">func</span> <span class="nf">localize</span><span class="p">(</span><span class="n">_</span> <span class="nv">string</span><span class="p">:</span> <span class="kt">String</span><span class="p">?)</span> <span class="o">-&gt;</span> <span class="kt">String</span><span class="p">?</span> <span class="p">{</span> <span class="k">guard</span> <span class="k">let</span> <span class="nv">term</span> <span class="o">=</span> <span class="n">string</span><span class="p">,</span> <span class="n">term</span><span class="o">.</span><span class="nf">hasPrefix</span><span class="p">(</span><span class="s">"@"</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="n">string</span> <span class="p">}</span> <span class="k">guard</span> <span class="o">!</span><span class="n">term</span><span class="o">.</span><span class="nf">hasPrefix</span><span class="p">(</span><span class="s">"@@"</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="n">term</span><span class="o">.</span><span class="nf">substring</span><span class="p">(</span><span class="nv">from</span><span class="p">:</span> <span class="n">term</span><span class="o">.</span><span class="nf">index</span><span class="p">(</span><span class="nv">after</span><span class="p">:</span> <span class="n">term</span><span class="o">.</span><span class="n">startIndex</span><span class="p">))</span> <span class="p">}</span> <span class="k">return</span> <span class="kt">NSLocalizedString</span><span class="p">(</span><span class="n">term</span><span class="o">.</span><span class="nf">substring</span><span class="p">(</span><span class="nv">from</span><span class="p">:</span> <span class="n">term</span><span class="o">.</span><span class="nf">index</span><span class="p">(</span><span class="nv">after</span><span class="p">:</span> <span class="n">term</span><span class="o">.</span><span class="n">startIndex</span><span class="p">)),</span> <span class="nv">comment</span><span class="p">:</span> <span class="s">""</span><span class="p">)</span> <span class="p">}</span> <span class="kd">public</span> <span class="kd">func</span> <span class="nf">localize</span><span class="p">(</span><span class="n">_</span> <span class="nv">string</span><span class="p">:</span> <span class="kt">String</span><span class="p">?,</span> <span class="n">_</span> <span class="nv">setter</span><span class="p">:</span> <span class="p">(</span><span class="kt">String</span><span class="p">?)</span> <span class="o">-&gt;</span> <span class="kt">Void</span><span class="p">)</span> <span class="p">{</span> <span class="nf">setter</span><span class="p">(</span><span class="nf">localize</span><span class="p">(</span><span class="n">string</span><span class="p">))</span> <span class="p">}</span> <span class="kd">public</span> <span class="kd">func</span> <span class="nf">localize</span><span class="p">(</span><span class="n">_</span> <span class="nv">getter</span><span class="p">:</span> <span class="p">(</span><span class="kt">UIControlState</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">String</span><span class="p">?,</span> <span class="n">_</span> <span class="nv">setter</span><span class="p">:</span> <span class="p">(</span><span class="kt">String</span><span class="p">?,</span> <span class="kt">UIControlState</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Void</span><span class="p">)</span> <span class="p">{</span> <span class="nf">setter</span><span class="p">(</span><span class="nf">localize</span><span class="p">(</span><span class="nf">getter</span><span class="p">(</span><span class="o">.</span><span class="n">normal</span><span class="p">)),</span> <span class="o">.</span><span class="n">normal</span><span class="p">)</span> <span class="nf">setter</span><span class="p">(</span><span class="nf">localize</span><span class="p">(</span><span class="nf">getter</span><span class="p">(</span><span class="o">.</span><span class="n">selected</span><span class="p">)),</span> <span class="o">.</span><span class="n">selected</span><span class="p">)</span> <span class="nf">setter</span><span class="p">(</span><span class="nf">localize</span><span class="p">(</span><span class="nf">getter</span><span class="p">(</span><span class="o">.</span><span class="n">highlighted</span><span class="p">)),</span> <span class="o">.</span><span class="n">highlighted</span><span class="p">)</span> <span class="nf">setter</span><span class="p">(</span><span class="nf">localize</span><span class="p">(</span><span class="nf">getter</span><span class="p">(</span><span class="o">.</span><span class="n">disabled</span><span class="p">)),</span> <span class="o">.</span><span class="n">disabled</span><span class="p">)</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <blockquote> <p><strong>Note:</strong> <code class="language-plaintext highlighter-rouge">substring</code> API is deprecated in Swift 4, should be replaced with <code class="language-plaintext highlighter-rouge">dropFirst</code> which also describes the original intent better.</p> </blockquote> <blockquote> <p><strong>Note:</strong> second localization helper should be upgraded to use the new KeyPath syntax when moving to Swift 4.</p> </blockquote> <p>Ok, so far, so good. Now let’s start implementing some localization. The process itself is clearly recursive where each container asks its children to localize themselves:</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">UIView</span><span class="p">:</span> <span class="kt">Localizable</span> <span class="p">{</span> <span class="kd">public</span> <span class="kd">func</span> <span class="nf">localize</span><span class="p">()</span> <span class="p">{</span> <span class="n">subviews</span><span class="o">.</span><span class="n">forEach</span> <span class="p">{</span> <span class="nv">$0</span><span class="o">.</span><span class="nf">localize</span><span class="p">()</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>Having implemented that, let’s add localization support for common controls required in most applications. The implementation is straightforward:</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">extension</span> <span class="kt">UILabel</span> <span class="p">{</span> <span class="kd">public</span> <span class="k">override</span> <span class="kd">func</span> <span class="nf">localize</span><span class="p">()</span> <span class="p">{</span> <span class="k">super</span><span class="o">.</span><span class="nf">localize</span><span class="p">()</span> <span class="nf">localize</span><span class="p">(</span><span class="n">text</span><span class="p">)</span> <span class="p">{</span> <span class="n">text</span> <span class="o">=</span> <span class="nv">$0</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="kd">public</span> <span class="kd">extension</span> <span class="kt">UIButton</span> <span class="p">{</span> <span class="kd">public</span> <span class="k">override</span> <span class="kd">func</span> <span class="nf">localize</span><span class="p">()</span> <span class="p">{</span> <span class="k">super</span><span class="o">.</span><span class="nf">localize</span><span class="p">()</span> <span class="nf">localize</span><span class="p">(</span><span class="nf">title</span><span class="p">(</span><span class="nv">for</span><span class="p">:),</span> <span class="nf">setTitle</span><span class="p">(</span><span class="nv">_</span><span class="p">:</span><span class="nv">for</span><span class="p">:))</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>Notice that for <code class="language-plaintext highlighter-rouge">UIButton</code> title we use another helper function which applies localization for all possible control states.</p> <p>Views are not the only objects we can configure with the Interface Builder. We should keep in mind the following objects too:</p> <ul> <li> <p><code class="language-plaintext highlighter-rouge">UIBarItem</code> with it’s subclasses: <code class="language-plaintext highlighter-rouge">UIBarButtonItem</code> and <code class="language-plaintext highlighter-rouge">UITabBarItem</code></p> </li> <li> <p><code class="language-plaintext highlighter-rouge">UINavigationItem</code></p> </li> </ul> <p>Any other objects you decide to use in your app</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">UIBarItem</span><span class="p">:</span> <span class="kt">Localizable</span> <span class="p">{</span> <span class="kd">public</span> <span class="kd">func</span> <span class="nf">localize</span><span class="p">()</span> <span class="p">{</span> <span class="nf">localize</span><span class="p">(</span><span class="n">title</span><span class="p">)</span> <span class="p">{</span> <span class="n">title</span> <span class="o">=</span> <span class="nv">$0</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="kd">public</span> <span class="kd">extension</span> <span class="kt">UIBarButtonItem</span> <span class="p">{</span> <span class="kd">public</span> <span class="k">override</span> <span class="kd">func</span> <span class="nf">localize</span><span class="p">()</span> <span class="p">{</span> <span class="k">super</span><span class="o">.</span><span class="nf">localize</span><span class="p">()</span> <span class="n">customView</span><span class="p">?</span><span class="o">.</span><span class="nf">localize</span><span class="p">()</span> <span class="p">}</span> <span class="p">}</span> <span class="kd">extension</span> <span class="kt">UINavigationItem</span><span class="p">:</span> <span class="kt">Localizable</span> <span class="p">{</span> <span class="kd">public</span> <span class="kd">func</span> <span class="nf">localize</span><span class="p">()</span> <span class="p">{</span> <span class="nf">localize</span><span class="p">(</span><span class="n">title</span><span class="p">)</span> <span class="p">{</span> <span class="n">title</span> <span class="o">=</span> <span class="nv">$0</span> <span class="p">}</span> <span class="nf">localize</span><span class="p">(</span><span class="n">prompt</span><span class="p">)</span> <span class="p">{</span> <span class="n">prompt</span> <span class="o">=</span> <span class="nv">$0</span> <span class="p">}</span> <span class="n">titleView</span><span class="p">?</span><span class="o">.</span><span class="nf">localize</span><span class="p">()</span> <span class="n">leftBarButtonItems</span><span class="p">?</span><span class="o">.</span><span class="n">forEach</span> <span class="p">{</span> <span class="nv">$0</span><span class="o">.</span><span class="nf">localize</span><span class="p">()</span> <span class="p">}</span> <span class="n">rightBarButtonItems</span><span class="p">?</span><span class="o">.</span><span class="n">forEach</span> <span class="p">{</span> <span class="nv">$0</span><span class="o">.</span><span class="nf">localize</span><span class="p">()</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>Finally, we have to start the flow somewhere. I usually rely on the following events:</p> <ul> <li> <p>Localize <code class="language-plaintext highlighter-rouge">title</code>, <code class="language-plaintext highlighter-rouge">navigationItem</code>, <code class="language-plaintext highlighter-rouge">tabBarItem</code> and <code class="language-plaintext highlighter-rouge">view</code> in the <code class="language-plaintext highlighter-rouge">viewDidLoad</code> method of my <code class="language-plaintext highlighter-rouge">UIViewController</code> subclasses.</p> </li> <li> <p>Localize contents of <code class="language-plaintext highlighter-rouge">UITableViewCell</code> and <code class="language-plaintext highlighter-rouge">UICollectionViewCell</code> subclasses in the corresponding <code class="language-plaintext highlighter-rouge">awakeFromNib</code> methods.</p> </li> </ul> <p>For the <code class="language-plaintext highlighter-rouge">UIViewController</code> here is a helper method to localize content, navigation and tab items:</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">UIViewController</span><span class="p">:</span> <span class="kt">Localizable</span> <span class="p">{</span> <span class="kd">public</span> <span class="kd">func</span> <span class="nf">localize</span><span class="p">()</span> <span class="p">{</span> <span class="nf">localize</span><span class="p">(</span><span class="n">title</span><span class="p">)</span> <span class="p">{</span> <span class="n">title</span> <span class="o">=</span> <span class="nv">$0</span> <span class="p">}</span> <span class="n">navigationItem</span><span class="o">.</span><span class="nf">localize</span><span class="p">()</span> <span class="n">tabBarItem</span><span class="p">?</span><span class="o">.</span><span class="nf">localize</span><span class="p">()</span> <span class="n">view</span><span class="o">.</span><span class="nf">localize</span><span class="p">()</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> Generic Optional Handling in Swift 2017-08-13T00:00:00+00:00 https://unpredictable.tech/2017/08/13/generic-optional-handling-in-swift/ <p>Sometimes you want to write a generic algorithm working on any <code class="language-plaintext highlighter-rouge">Optional</code> type no matter what actual type is wrapped inside. Ok, this can be easily done with a free generic function, but what if you want to write a <code class="language-plaintext highlighter-rouge">Sequence</code> extension to remove all <code class="language-plaintext highlighter-rouge">nil</code> values for example?</p> <p>Things get a little bit complicated here since <code class="language-plaintext highlighter-rouge">Optional</code> is not a protocol but a concrete type and so it can’t be used as a generic type constraint.</p> <!-- more --> <p>Generic protocols and concrete types in Swift serve different purposes: we create instances and declare variables of concrete types while protocols can be used as a generic type constraints.</p> <p>Type erasure is a technique used in case we want to declare a variable able to hold any concrete type conforming to a specific protocol. What we want here instead is another part of the same puzzle, we need a protocol allowing us to use the concrete generic type as a constraint.</p> <p>This may sound complicated in English but, as it usually happens, looks much simpler when written in plain Swift 😄</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">protocol</span> <span class="kt">OptionalType</span><span class="p">:</span> <span class="kt">ExpressibleByNilLiteral</span> <span class="p">{</span> <span class="kd">associatedtype</span> <span class="kt">WrappedType</span> <span class="k">var</span> <span class="nv">asOptional</span><span class="p">:</span> <span class="kt">WrappedType</span><span class="p">?</span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span> <span class="p">}</span> <span class="kd">extension</span> <span class="kt">Optional</span><span class="p">:</span> <span class="kt">OptionalType</span> <span class="p">{</span> <span class="kd">public</span> <span class="k">var</span> <span class="nv">asOptional</span><span class="p">:</span> <span class="kt">Wrapped</span><span class="p">?</span> <span class="p">{</span> <span class="k">return</span> <span class="k">self</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>So here we just define an <code class="language-plaintext highlighter-rouge">OptionalType</code> protocol declaring <code class="language-plaintext highlighter-rouge">WrappedType</code> associated type and conform <code class="language-plaintext highlighter-rouge">Optional</code> enum to it. Note that generic type parameters do not automatically fulfill protocol requirements, but we have type inference covering us here.</p> <p>In order to make the protocol useful we expose some basic <code class="language-plaintext highlighter-rouge">Optional</code> functionality:</p> <ul> <li> <p><code class="language-plaintext highlighter-rouge">asOptional</code> property gives us an access to the optional binding syntax.</p> </li> <li> <p><code class="language-plaintext highlighter-rouge">ExpressibleByNilLiteral</code> conformance allows us to use nil for initialization.</p> </li> </ul> <p>Having done that, we can now use <code class="language-plaintext highlighter-rouge">OptionalType</code> as a generic constraint:</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">extension</span> <span class="kt">Sequence</span> <span class="k">where</span> <span class="k">Self</span><span class="o">.</span><span class="kt">Iterator</span><span class="o">.</span><span class="kt">Element</span><span class="p">:</span> <span class="kt">OptionalType</span> <span class="p">{</span> <span class="kd">public</span> <span class="kd">func</span> <span class="nf">removingNils</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="k">Self</span><span class="o">.</span><span class="kt">Iterator</span><span class="o">.</span><span class="kt">Element</span><span class="o">.</span><span class="kt">WrappedType</span><span class="p">]</span> <span class="p">{</span> <span class="k">return</span> <span class="n">flatMap</span> <span class="p">{</span> <span class="nv">$0</span><span class="o">.</span><span class="n">asOptional</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>If you are looking for more tricks that can be done with the <code class="language-plaintext highlighter-rouge">Optional</code> enum, you should check <a href="http://www.russbishop.net/improving-optionals">an excellent article by Russ Bishop</a>.</p> Using Self in Swift Class Extensions 2017-08-10T00:00:00+00:00 https://unpredictable.tech/2017/08/10/using-self-in-swift-class-extensions/ <p>It might be tempting to use <code class="language-plaintext highlighter-rouge">Self</code> as a parameter type when extending classes but Swift only allows it in a protocol or as the result of a class method invocation.</p> <p>In fact, this is a semantically correct restriction for non-final classes in most cases, except when we want to use <code class="language-plaintext highlighter-rouge">Self</code> as an argument of the closure, think about completion handlers for example. In that case <code class="language-plaintext highlighter-rouge">Self</code> is used just as an another method call result which is absolutely valid option.</p> <!-- more --> <p>My favorite example here is the continuation helper for the Operation class:</p> <ul> <li> <p>Using continuation operation instead of completion block gives control over its execution context.</p> </li> <li> <p>I tend to define result accessors in my Operation subclasses and I usually want to access them in a continuation.</p> </li> <li> <p>Accessing operation from the continuation does not introduce a retain cycle because any operation already owns its dependencies.</p> </li> </ul> <p>So we need to pass a block receiving operation that has just finished execution as a parameter. That is not allowed in a class extension.</p> <p>Hm, but we can use Self that way in a protocol, right… Protocols to the rescue! The plan is to define a dummy protocol, conform our class to it and extend the protocol instead of the class.</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">protocol</span> <span class="kt">BlockContinuable</span> <span class="p">{</span> <span class="p">}</span> <span class="kd">extension</span> <span class="kt">Operation</span><span class="p">:</span> <span class="kt">BlockContinuable</span> <span class="p">{</span> <span class="p">}</span> <span class="kd">public</span> <span class="kd">extension</span> <span class="kt">BlockContinuable</span> <span class="k">where</span> <span class="k">Self</span><span class="p">:</span> <span class="kt">Operation</span> <span class="p">{</span> <span class="kd">public</span> <span class="kd">func</span> <span class="p">`</span><span class="nv">continue</span><span class="p">`(</span><span class="n">on</span> <span class="nv">queue</span><span class="p">:</span> <span class="kt">OperationQueue</span> <span class="o">=</span> <span class="o">.</span><span class="n">main</span><span class="p">,</span> <span class="n">with</span> <span class="nv">block</span><span class="p">:</span> <span class="kd">@escaping</span> <span class="p">(</span><span class="k">Self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Void</span><span class="p">)</span> <span class="p">{</span> <span class="k">let</span> <span class="nv">continuation</span> <span class="o">=</span> <span class="kt">BlockOperation</span> <span class="p">{</span> <span class="nf">block</span><span class="p">(</span><span class="k">self</span><span class="p">)</span> <span class="p">}</span> <span class="n">continuation</span><span class="o">.</span><span class="nf">addDependency</span><span class="p">(</span><span class="k">self</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">continuation</span><span class="p">)</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>Now we can use our <code class="language-plaintext highlighter-rouge">continue(on:with:)</code> extension and access operation result in a type-safe manner:</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="kt">SampleOperation</span><span class="p">:</span> <span class="kt">Operation</span> <span class="p">{</span> <span class="kd">public</span> <span class="k">var</span> <span class="nv">result</span><span class="p">:</span> <span class="kt">String</span><span class="p">?</span> <span class="kd">public</span> <span class="k">override</span> <span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> <span class="n">result</span> <span class="o">=</span> <span class="s">"Hello, Continuation!"</span> <span class="p">}</span> <span class="p">}</span> <span class="k">let</span> <span class="nv">operation</span> <span class="o">=</span> <span class="kt">SampleOperation</span><span class="p">()</span> <span class="n">operation</span><span class="o">.</span><span class="k">continue</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="nv">$0</span><span class="o">.</span><span class="n">result</span> <span class="p">??</span> <span class="s">"operation failed"</span><span class="p">)</span> <span class="p">}</span> <span class="kt">OperationQueue</span><span class="o">.</span><span class="n">main</span><span class="o">.</span><span class="nf">addOperation</span><span class="p">(</span><span class="n">operation</span><span class="p">)</span> </code></pre></div></div> Managing Temporary Files in Swift 2017-08-08T00:00:00+00:00 https://unpredictable.tech/2017/08/08/managing-temporary-files-in-swift/ <p>Reference counting is great in taking unused objects out of memory. The same can be applied to the temporary files we allocate.</p> <!-- more --> <p>Let’s say you are building a video sharing app, here is what you have to do each time user decides to upload some content from the app:</p> <ul> <li> <p>Take a movie and store into a temporary file.</p> </li> <li> <p>Present filter/crop/whatever editing UI to the user.</p> </li> <li> <p>Render the final video to another temporary file, the first file can be safely removed by now.</p> </li> <li> <p>Upload video to the cloud and remove the second file when done.</p> </li> </ul> <p>Taking care of those files manually adds extra complexity and can be easily overseen resulting in an excessive storage use caused by the lost data. I faced this myself when my pet project suddenly took over all my storage 😂</p> <p>Reference counting comes to the rescue here: we can create <code class="language-plaintext highlighter-rouge">TemporaryFileURL</code> class wrapping regular <code class="language-plaintext highlighter-rouge">URL</code> and performing cleanup in its deinit method. Now the temporary file will be removed automatically when it becomes unreferenced from the application.</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">final</span> <span class="kd">class</span> <span class="kt">TemporaryFileURL</span><span class="p">:</span> <span class="kt">ManagedURL</span> <span class="p">{</span> <span class="kd">public</span> <span class="k">let</span> <span class="nv">contentURL</span><span class="p">:</span> <span class="kt">URL</span> <span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="kd">extension</span> <span class="nv">ext</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="p">{</span> <span class="n">contentURL</span> <span class="o">=</span> <span class="kt">URL</span><span class="p">(</span><span class="nv">fileURLWithPath</span><span class="p">:</span> <span class="kt">NSTemporaryDirectory</span><span class="p">())</span> <span class="o">.</span><span class="nf">appendingPathComponent</span><span class="p">(</span><span class="kt">UUID</span><span class="p">()</span><span class="o">.</span><span class="n">uuidString</span><span class="p">)</span> <span class="o">.</span><span class="nf">appendingPathExtension</span><span class="p">(</span><span class="n">ext</span><span class="p">)</span> <span class="p">}</span> <span class="kd">deinit</span> <span class="p">{</span> <span class="kt">DispatchQueue</span><span class="o">.</span><span class="nf">global</span><span class="p">(</span><span class="nv">qos</span><span class="p">:</span> <span class="o">.</span><span class="n">utility</span><span class="p">)</span><span class="o">.</span><span class="n">async</span> <span class="p">{</span> <span class="p">[</span><span class="n">contentURL</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="n">contentURL</span><span class="p">]</span> <span class="k">in</span> <span class="k">try</span><span class="p">?</span> <span class="kt">FileManager</span><span class="o">.</span><span class="k">default</span><span class="o">.</span><span class="nf">removeItem</span><span class="p">(</span><span class="nv">at</span><span class="p">:</span> <span class="n">contentURL</span><span class="p">)</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>Notice the rarely used capture with assignment syntax I used here to pass <code class="language-plaintext highlighter-rouge">contentURL</code> for the deferred file cleanup: we’d better not capture <code class="language-plaintext highlighter-rouge">self</code> during deallocation so we just copy the underlying file URL and let <code class="language-plaintext highlighter-rouge">self</code> deallocate properly.</p> <p>Now you may want to unify all the code working with files no matter if they are normal or temporary. That’s what <code class="language-plaintext highlighter-rouge">ManagedURL</code> protocol stands here for. We can conform <code class="language-plaintext highlighter-rouge">URL</code> struct and/or <code class="language-plaintext highlighter-rouge">NSURL</code> class to the protocol and pass <code class="language-plaintext highlighter-rouge">ManagedURL</code> references all around.</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">protocol</span> <span class="kt">ManagedURL</span> <span class="p">{</span> <span class="k">var</span> <span class="nv">contentURL</span><span class="p">:</span> <span class="kt">URL</span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span> <span class="kd">func</span> <span class="nf">keepAlive</span><span class="p">()</span> <span class="p">}</span> <span class="kd">public</span> <span class="kd">extension</span> <span class="kt">ManagedURL</span> <span class="p">{</span> <span class="kd">public</span> <span class="kd">func</span> <span class="nf">keepAlive</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span> <span class="p">}</span> <span class="kd">extension</span> <span class="kt">URL</span><span class="p">:</span> <span class="kt">ManagedURL</span> <span class="p">{</span> <span class="kd">public</span> <span class="k">var</span> <span class="nv">contentURL</span><span class="p">:</span> <span class="kt">URL</span> <span class="p">{</span> <span class="k">return</span> <span class="k">self</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>I have also added a no-op <code class="language-plaintext highlighter-rouge">keepAlive</code> function whose sole purpose it to allow easy object capture by various closures used as a completion handlers. This allows us to keep file while background operation is executed like this:</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">URLSession</span><span class="o">.</span><span class="n">shared</span><span class="o">.</span><span class="nf">uploadTask</span><span class="p">(</span><span class="nv">with</span><span class="p">:</span> <span class="n">request</span><span class="p">,</span> <span class="nv">fromFile</span><span class="p">:</span> <span class="n">fileToUpload</span><span class="o">.</span><span class="n">contentURL</span><span class="p">)</span> <span class="p">{</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span> <span class="k">in</span> <span class="n">temporaryFile</span><span class="o">.</span><span class="nf">keepAlive</span><span class="p">()</span> <span class="p">}</span> </code></pre></div></div> <p>As <a href="https://twitter.com/ketzusaka">James Richard</a> reminded on Twitter, this technique is called <a href="https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization">Resource Acquisition is Initialization</a>, or RAII, and has beed commonly used in C++ for ages. The trick is good for managing lifetime of any external resource but is often overlooked since destructors are not very common in the modern world of ARC, GC, defer, try/finally and others.</p> Storyboard Tricks 2017-08-06T00:00:00+00:00 https://unpredictable.tech/2017/08/06/storyboard-tricks/ <p>While looking through another sample project today, I have once again noticed how many string literals and force downcasts accompany most of the storyboard-related code.</p> <p>Having realized that, I decided to share a few practices I use myself to make my view controller and storyboard handling code more conscious.</p> <!-- more --> <h2 id="instantiating-view-controllers">Instantiating View Controllers</h2> <p>Let’s start with the instantiation process: we rely on file name to create <code class="language-plaintext highlighter-rouge">UIStoryboard</code> instance, then we use view controller identifier to call <code class="language-plaintext highlighter-rouge">instantiateViewController(withIdentifier:)</code> and all this mess is followed by the force downcast to the actual type.</p> <p>View controller instantiation code should be much cleaner, here is an example of what I’m looking for:</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kd">func</span> <span class="nf">presentDetails</span><span class="p">(</span><span class="k">for</span> <span class="nv">item</span><span class="p">:</span> <span class="kt">ItemModel</span><span class="p">)</span> <span class="p">{</span> <span class="k">let</span> <span class="nv">viewController</span> <span class="o">=</span> <span class="kt">DetailsViewController</span><span class="o">.</span><span class="nf">makeWithStoryboard</span><span class="p">()</span> <span class="n">viewController</span><span class="o">.</span><span class="n">delegate</span> <span class="o">=</span> <span class="k">self</span> <span class="n">viewController</span><span class="o">.</span><span class="n">model</span> <span class="o">=</span> <span class="n">item</span> <span class="nf">present</span><span class="p">(</span><span class="n">viewController</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></div></div> <p>To achieve that we have to avoid “massive storyboard” pattern and keep each flow in a separate file. Having done that we can establish simple conventions allowing us to hide all that UIStoryboard stuff in a helper function:</p> <ul> <li> <p>Only instantiate initial view controllers from code.</p> </li> <li> <p>Access all other view controllers in the same storyboard via segues.</p> </li> <li> <p>Use initial view controller class name to name the corresponding storyboard. This is obvious for good old NIBs but we tend to not use the pattern with storyboards.</p> </li> </ul> <p>After establishing the conventions we can easily instantiate any view controller given only its class, everything else is derived programmatically. Helper function code should be something similar to the following snippet:</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kd">private</span> <span class="kd">func</span> <span class="n">dynamicCast</span><span class="o">&lt;</span><span class="kt">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">_</span> <span class="nv">object</span><span class="p">:</span> <span class="kt">Any</span><span class="p">,</span> <span class="nv">as</span><span class="p">:</span> <span class="kt">T</span><span class="o">.</span><span class="k">Type</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">T</span><span class="p">?</span> <span class="p">{</span> <span class="k">return</span> <span class="n">object</span> <span class="k">as?</span> <span class="kt">T</span> <span class="p">}</span> <span class="kd">public</span> <span class="kd">extension</span> <span class="kt">UIViewController</span> <span class="p">{</span> <span class="kd">public</span> <span class="kd">static</span> <span class="kd">func</span> <span class="nf">makeWithStoryboard</span><span class="p">(</span><span class="n">_</span> <span class="nv">name</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">bundle</span><span class="p">:</span> <span class="kt">Bundle</span><span class="p">?</span> <span class="o">=</span> <span class="kc">nil</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="k">Self</span> <span class="p">{</span> <span class="k">let</span> <span class="nv">name</span> <span class="o">=</span> <span class="n">name</span> <span class="p">??</span> <span class="kt">String</span><span class="p">(</span><span class="nv">describing</span><span class="p">:</span> <span class="k">self</span><span class="p">)</span><span class="o">.</span><span class="nf">components</span><span class="p">(</span><span class="nv">separatedBy</span><span class="p">:</span> <span class="s">"."</span><span class="p">)</span><span class="o">.</span><span class="n">last</span><span class="o">!</span> <span class="k">let</span> <span class="nv">bundle</span> <span class="o">=</span> <span class="n">bundle</span> <span class="p">??</span> <span class="kt">Bundle</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="k">self</span><span class="p">)</span> <span class="k">guard</span> <span class="n">bundle</span><span class="o">.</span><span class="nf">url</span><span class="p">(</span><span class="nv">forResource</span><span class="p">:</span> <span class="n">name</span><span class="p">,</span> <span class="nv">withExtension</span><span class="p">:</span> <span class="s">"storyboardc"</span><span class="p">)</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="k">else</span> <span class="p">{</span> <span class="nf">fatalError</span><span class="p">(</span><span class="s">"Can't find storyboard named `</span><span class="se">\(</span><span class="n">name</span><span class="se">)</span><span class="s">` in bundle `</span><span class="se">\(</span><span class="n">bundle</span><span class="se">)</span><span class="s">`."</span><span class="p">)</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="n">name</span><span class="p">,</span> <span class="nv">bundle</span><span class="p">:</span> <span class="n">bundle</span><span class="p">)</span> <span class="k">guard</span> <span class="k">let</span> <span class="nv">initialViewController</span> <span class="o">=</span> <span class="n">storyboard</span><span class="o">.</span><span class="nf">instantiateInitialViewController</span><span class="p">()</span> <span class="k">else</span> <span class="p">{</span> <span class="nf">fatalError</span><span class="p">(</span><span class="s">"No initial view controller defined in storyboard `</span><span class="se">\(</span><span class="n">name</span><span class="se">)</span><span class="s">`, bundle `</span><span class="se">\(</span><span class="n">bundle</span><span class="se">)</span><span class="s">`."</span><span class="p">)</span> <span class="p">}</span> <span class="k">guard</span> <span class="k">let</span> <span class="nv">resultViewController</span> <span class="o">=</span> <span class="nf">dynamicCast</span><span class="p">(</span><span class="n">initialViewController</span><span class="p">,</span> <span class="nv">as</span><span class="p">:</span> <span class="k">self</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span> <span class="nf">fatalError</span><span class="p">(</span><span class="s">"Wrong initial view controller found in storyboard `</span><span class="se">\(</span><span class="n">name</span><span class="se">)</span><span class="s">`, bundle `</span><span class="se">\(</span><span class="n">bundle</span><span class="se">)</span><span class="s">`: expected `</span><span class="se">\(</span><span class="k">self</span><span class="se">)</span><span class="s">`, found `</span><span class="se">\(</span><span class="nf">type</span><span class="p">(</span><span class="nv">of</span><span class="p">:</span> <span class="n">initialViewController</span><span class="p">)</span><span class="se">)</span><span class="s">`."</span><span class="p">)</span> <span class="p">}</span> <span class="k">return</span> <span class="n">resultViewController</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>I’d like to note two tricky moments here:</p> <ul> <li> <p>We use <code class="language-plaintext highlighter-rouge">Bundle(for: self)</code> instead of <code class="language-plaintext highlighter-rouge">nil</code> or <code class="language-plaintext highlighter-rouge">Bundle.main</code> to properly load resources from dynamic frameworks.</p> </li> <li> <p>That weird <code class="language-plaintext highlighter-rouge">dynamicCast(_:as:)</code> function helps us to work around Swift limitation which prohibits us to write <code class="language-plaintext highlighter-rouge">initialViewController as? Self</code>. Doing the former results in a compile error stating that “<code class="language-plaintext highlighter-rouge">Self</code> is only available in a protocol or as the result of a method in a class”.</p> </li> </ul> <h2 id="handling-segues">Handling Segues</h2> <p>Another popular place to find string identifiers when working with storyboards is <code class="language-plaintext highlighter-rouge">prepare(for:sender:)</code> method. Here we test segue identifier and downcast view controller depending on that. Something like this:</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">override</span> <span class="kd">func</span> <span class="nf">prepare</span><span class="p">(</span><span class="k">for</span> <span class="nv">segue</span><span class="p">:</span> <span class="kt">UIStoryboardSegue</span><span class="p">,</span> <span class="nv">sender</span><span class="p">:</span> <span class="kt">Any</span><span class="p">?)</span> <span class="p">{</span> <span class="k">if</span> <span class="n">segue</span><span class="o">.</span><span class="n">identifier</span> <span class="o">==</span> <span class="s">"OpenAnotherViewController"</span> <span class="p">{</span> <span class="k">let</span> <span class="nv">destination</span> <span class="o">=</span> <span class="n">segue</span><span class="o">.</span><span class="n">destination</span> <span class="k">as!</span> <span class="kt">AnotherViewController</span> <span class="n">destination</span><span class="o">.</span><span class="n">delegate</span> <span class="o">=</span> <span class="k">self</span> <span class="k">return</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>The snippet employs two bad practices at once: error-prone string identifier comparison followed by force downcast.</p> <p>It seems to me that the only thing that actually matters in most cases is our destination view controller type. In those cases we can simply switch over the possible destinations and <code class="language-plaintext highlighter-rouge">fatalError</code> for everything else. The following code snippet shows how this may look in your code:</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">override</span> <span class="kd">func</span> <span class="nf">prepare</span><span class="p">(</span><span class="k">for</span> <span class="nv">segue</span><span class="p">:</span> <span class="kt">UIStoryboardSegue</span><span class="p">,</span> <span class="nv">sender</span><span class="p">:</span> <span class="kt">Any</span><span class="p">?)</span> <span class="p">{</span> <span class="k">switch</span> <span class="n">segue</span><span class="o">.</span><span class="n">destination</span> <span class="p">{</span> <span class="k">case</span> <span class="k">let</span> <span class="nv">destination</span> <span class="k">as</span> <span class="kt">AnotherViewController</span><span class="p">:</span> <span class="n">destination</span><span class="o">.</span><span class="n">delegate</span> <span class="o">=</span> <span class="k">self</span> <span class="k">default</span><span class="p">:</span> <span class="nf">fatalError</span><span class="p">(</span><span class="s">"Unexpected segue: `</span><span class="se">\(</span><span class="k">self</span><span class="se">)</span><span class="s">` -&gt; `</span><span class="se">\(</span><span class="n">segue</span><span class="o">.</span><span class="n">destination</span><span class="se">)</span><span class="s">`"</span><span class="p">)</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div>