supersonicbyte swifty bytes 2025-01-30T00:00:00Z https://supersonicbyte.com/ Mirza Swift interoperability with C (Part 1) 2025-01-30T00:00:00Z https://supersonicbyte.com/blog/swift-interoperability-with-c/ <h2 id="intro">Intro</h2> <p>The C programming language is the backbone of modern computing. With its assembly-close nature offering speed and effectiveness it's still the main choice when writing system software since it's beginning is the '70s. Most operating systems are written in C. For any new programming language, it's crucial to have a way to communicate and integrate with existing C code.</p> <p>Swift comes with exceptional interoperability with Objective-C/C and most recently with C++.</p> <p>This is the first part of a series of blog posts dedicated to Swift's interoperability with C. In this post we're going to explore how does actually Swift call into C code.</p> <h2 id="creating-a-simple-c-library">Creating a simple C library</h2> <p>Let's create a simple C library. Consider the following C code:</p> <pre class="language-c" tabindex="0"><code class="language-c"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdio.h></span></span> <span class="token keyword">void</span> <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"foo!\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre> <p>And the header:</p> <pre class="language-c" tabindex="0"><code class="language-c"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">ifndef</span> <span class="token expression">FOO_H</span></span> <span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name">FOO_H</span></span> <span class="token keyword">void</span> <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">endif</span></span></code></pre> <blockquote> <p>Note: The #ifndef and #define and the beginning are called <a href="https://en.wikipedia.org/wiki/Include_guard">include guards</a>.</p> </blockquote> <p>Pretty straightforward. We define a simple function <code>foo</code> which prints to the standard output.</p> <p>Let's compile it:</p> <pre class="language-c" tabindex="0"><code class="language-c">clang <span class="token operator">-</span>c foo<span class="token punctuation">.</span>c</code></pre> <p>This will compile our code and produce a <code>foo.o</code> object file, which contains the actual machine code. If we run:</p> <blockquote> <p>Note: I'm on a macbook and I'm using the Clang compiler.</p> </blockquote> <pre class="language-c" tabindex="0"><code class="language-c">file foo<span class="token punctuation">.</span>o</code></pre> <p>We get:</p> <pre class="language-c" tabindex="0"><code class="language-c">Mach<span class="token operator">-</span>O <span class="token number">64</span><span class="token operator">-</span>bit object arm64</code></pre> <p>Now, we just need to archive the file to make it into a <em>static library</em>. We can do so by running:</p> <pre class="language-c" tabindex="0"><code class="language-c">ar <span class="token operator">-</span>rv libfoo<span class="token punctuation">.</span>a foo<span class="token punctuation">.</span>o</code></pre> <blockquote> <p>A static library is an archive of .o files with a .a extension</p> </blockquote> <p>Great! Now let's try to use our newly created library in a test program:</p> <pre class="language-c" tabindex="0"><code class="language-c"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdio.h></span></span> <span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">"foo.h"</span></span> <span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token keyword">int</span> argc<span class="token punctuation">,</span> <span class="token keyword">char</span> <span class="token operator">*</span>argv<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre> <p>We can now compile and run:</p> <pre class="language-c" tabindex="0"><code class="language-c">clang test<span class="token punctuation">.</span>c libfoo<span class="token punctuation">.</span>a <span class="token operator">-</span>o Test <span class="token punctuation">.</span><span class="token operator">/</span>Test foo<span class="token operator">!</span></code></pre> <p>Alright, our C library is working well. Now we can try to call it from Swift!</p> <h2 id="importing-c-in-swift">Importing C in Swift</h2> <p>Create a new swift file, name it <code>main.swift</code>.</p> <pre class="language-swift" tabindex="0"><code class="language-swift"><span class="token function">print</span><span class="token punctuation">(</span><span class="token string-literal"><span class="token string">"Hello from Swift!"</span></span><span class="token punctuation">)</span> <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token function">print</span><span class="token punctuation">(</span><span class="token string-literal"><span class="token string">"Goodbye!"</span></span><span class="token punctuation">)</span></code></pre> <p>We just referenced the <code>foo</code> function as if it was imported, but naively trying to compile it with <code>swiftc</code> will fail. We are referencing <code>foo</code> which hasn't been declared anywhere. We have to express that <code>foo</code> should be imported from our <code>libfoo</code> C library. In our C program we did so by including the <code>foo.h</code> header which declares our <code>foo</code> function.</p> <p>One way of doing it is to pass the header file to the compiler with the <code>-import-objc-header</code> flag. The other, preferred way, is with Clang modules. Clang modules are a concept from the Clang compiler. They bring a lot of improvements and solve pain points of plain header files. We won't go in detail right here, but if you want to know more about them you can find it in the <a href="https://clang.llvm.org/docs/Modules.html">documentation</a>.</p> <p>Let's create a clang module for our <code>foo.h</code> by making a file named <code>module.modulemap</code> and setting it's contents to:</p> <pre class="language-c" tabindex="0"><code class="language-c">module CFoo <span class="token punctuation">{</span> header <span class="token string">"foo.h"</span> export <span class="token operator">*</span> <span class="token punctuation">}</span></code></pre> <p>The modulemap is pretty simple, it declares a module named <code>CFoo</code> (it's a convention that we add a prefix C for C modules that we import in Swift) references our <code>foo.h</code> header and exports everything from it.</p> <p>Now, let's import this module we created by in our <code>main.swift</code> by adding this on the top:</p> <pre class="language-swift" tabindex="0"><code class="language-swift"><span class="token keyword">import</span> <span class="token class-name">CFoo</span></code></pre> <p>Great! We are all set. The last thing to do is to invoke the swift compiler and pass all the required information for it to compile and link our <code>main.swift</code>:</p> <pre class="language-c" tabindex="0"><code class="language-c">swiftc <span class="token operator">-</span>I <span class="token punctuation">.</span><span class="token operator">/</span> <span class="token operator">-</span>L <span class="token punctuation">.</span><span class="token operator">/</span> <span class="token operator">-</span>lfoo main<span class="token punctuation">.</span>swift <span class="token operator">-</span>o main </code></pre> <blockquote> <p>Note: If we wanted to use plain header we would invoke the compiler like this: swiftc -import-objc-header foo.h -I ./ -L ./ -lfoo main.swift -o main</p> </blockquote> <p>Voila! And if we run our executable we get:</p> <pre class="language-c" tabindex="0"><code class="language-c"><span class="token punctuation">.</span><span class="token operator">/</span>main Hello form swift<span class="token operator">!</span> foo<span class="token operator">!</span> Goodbye<span class="token operator">!</span></code></pre> <p>Everything works!</p> <p>Let's break down the flags we are sending to the compiler:</p> <ul> <li><code>-I</code> flag enables us to pass a directory which will be used as the <code>import search path</code>, basically a place where it tries to resolve our imports, we pass the current directory (because we created our <code>module.modulemap</code> in the same directory as our <code>main.swift</code>)</li> <li><code>-L</code> flag enable us to pass a directory which will be used as the <code>library link search path</code>, a place where to look for the libraries we are linking against, again we pass the current directory for the same reasons</li> <li><code>-lfoo</code> flag is passed down to the linker and it tells the linker to search for a library named <code>libfoo</code> in the library search path. More about this flag can be found in the man pages of ld.</li> <li><code>main.swift</code> the source file we are compiling</li> <li><code>-o</code> specifies the name of our output</li> </ul> <p>Let's do some unwinding. On a high level, the swift compiler parses the passed in clang modules and transforms the declarations in the C headers to Swift declarations. The result of this is that our <code>foo</code> from C will end up as a swift declaration:</p> <pre class="language-swift" tabindex="0"><code class="language-swift"><span class="token keyword">func</span> <span class="token function-definition function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span></code></pre> <p>But these imported C functions get very different treatment during the compilation from Swift functions. Their names don't get mangled so they preserve C calling convention (more on that later). Mangling is the process of encoding functions and variable names into unique names that are later on useful in the linking process. One thing mangling enables for us is function overloading, since every argument will be encoded into the mangled name thus allowing us to overload functions.</p> <p>For example, consider the following function:</p> <pre class="language-swift" tabindex="0"><code class="language-swift"><span class="token keyword">func</span> <span class="token function-definition function">bar</span><span class="token punctuation">(</span>arg<span class="token punctuation">:</span> <span class="token class-name">Int</span><span class="token punctuation">)</span></code></pre> <p>After mangling the function name will be:</p> <pre class="language-swift" tabindex="0"><code class="language-swift">$s3barAA3argySi_tF</code></pre> <p>To demangle a name we can use:</p> <pre class="language-swift" tabindex="0"><code class="language-swift">swift demangle s3barAA3argySi_tF <span class="token comment">// it prints out the following</span> $s3barAA3argySi_tF <span class="token operator">---></span> bar<span class="token punctuation">.</span><span class="token function">bar</span><span class="token punctuation">(</span>arg<span class="token punctuation">:</span> <span class="token class-name">Swift</span><span class="token punctuation">.</span><span class="token class-name">Int</span><span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token punctuation">(</span><span class="token punctuation">)</span></code></pre> <p>(Note that we dropped the $ prefix since that's a special character in most shells)</p> <p>We mentioned that we want to perserve C calling convention. The calling convention defines the following:</p> <ul> <li>how the arguments are passed onto the stack</li> <li>who will clear the stack, caller or callee?</li> <li>what registers will be used and how</li> </ul> <p>Basically, it defines the exact way how we should setup our machine code when calling machine code that was outputed from compilaton our C code. For any two pieces of machine code that interact, it's important that the convention is aligned in other for the caller and callee to work as expected. That being said, in order for machine code generated code from Swift to correctly call a machine code generated from a C source it's important to follow the C calling convention. The convention could possibly differ regarding which underlying platform is targeted but it's important that the conventions are aligned. C doesn't support function overloading and mangling, and that's way it's important that this function name doesn't get mangled. Along the way the imported C functions also get annotated with <code>@convention(c)</code> (this can be seen if we print out the SIL <em>Swift Intermidiate Language</em>). This is also plays into the compiler treating these functions <em>specially</em> so it can generate adequate machine code which follows the calling convention.</p> <p>Since we are linking statically against our <code>libfoo</code> the linker will actually embed the code that we are referencing from library into the executable and then the resolve the <code>foo</code> call with an actual memory address of the function.</p> <p>Let's confirm this by examining the executable:</p> <pre class="language-c" tabindex="0"><code class="language-c">objdump <span class="token operator">-</span>d main <span class="token operator">|</span> grep <span class="token char">'foo'</span></code></pre> <p>This will disassemble the machine code into assembly and then we search for the foo symbol. And we can see:</p> <pre class="language-c" tabindex="0"><code class="language-c"><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token number">100003</span>d94<span class="token operator">:</span> <span class="token number">94000056</span> bl <span class="token number">0x100003eec</span> <span class="token operator">&lt;</span>_foo<span class="token operator">></span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span></code></pre> <p>This is the place in our <code>main.swift</code> where we are calling <code>foo</code>. In the assembly <code>bl</code> denotes an arm64 (this could be different depending on what platform are you executing e.g. Intel Mac) instruction called branch and link. Before branching the instruction stores the current address into the link register which is used to keep track to which address to return when the <code>ret</code> instruction is being executed. The instruction has one argument which determines the address to which the execution should be branched.</p> <p>If we search again we can find the definition of <code>foo</code> in the assembly.</p> <pre class="language-c" tabindex="0"><code class="language-c"><span class="token number">0000000100003</span>eec <span class="token operator">&lt;</span>_foo<span class="token operator">></span><span class="token operator">:</span> <span class="token number">100003</span>eec<span class="token operator">:</span> a9bf7bfd stp x29<span class="token punctuation">,</span> x30<span class="token punctuation">,</span> <span class="token punctuation">[</span>sp<span class="token punctuation">,</span> #<span class="token operator">-</span><span class="token number">0x10</span><span class="token punctuation">]</span><span class="token operator">!</span> <span class="token number">100003</span>ef0<span class="token operator">:</span> <span class="token number">910003f</span>d mov x29<span class="token punctuation">,</span> sp <span class="token number">100003</span>ef4<span class="token operator">:</span> <span class="token number">90000000</span> adrp x0<span class="token punctuation">,</span> <span class="token number">0x100003000</span> <span class="token operator">&lt;</span>_swift_bridgeObjectRelease<span class="token operator">+</span><span class="token number">0x100003000</span><span class="token operator">></span> <span class="token number">100003</span>ef8<span class="token operator">:</span> <span class="token number">913</span>dbc00 add x0<span class="token punctuation">,</span> x0<span class="token punctuation">,</span> #<span class="token number">0xf6f</span> <span class="token number">100003</span>efc<span class="token operator">:</span> <span class="token number">9400000f</span> bl <span class="token number">0x100003f38</span> <span class="token operator">&lt;</span>_swift_bridgeObjectRelease<span class="token operator">+</span><span class="token number">0x100003f38</span><span class="token operator">></span> <span class="token number">100003f</span><span class="token number">00</span><span class="token operator">:</span> a8c17bfd ldp x29<span class="token punctuation">,</span> x30<span class="token punctuation">,</span> <span class="token punctuation">[</span>sp<span class="token punctuation">]</span><span class="token punctuation">,</span> #<span class="token number">0x10</span> <span class="token number">100003f</span><span class="token number">04</span><span class="token operator">:</span> d65f03c0 ret</code></pre> <p>After all the instructions are executed we hit the <code>ret</code> instructions which takes us back to the address stored in the link register, meaning we finished the with the call of <code>foo</code> and we are returning to execution of our <code>main.swift</code>.</p> <p>And at this point exactly we understand how the Swift code called our C function.</p> <h2 id="final-notes">Final notes</h2> <p>Interoperability boils down to making the machine code generated from different languages play nicely with each other. A lot of heavy lifting is done inside the Swift compiler, which seamlessly translates the C declarations and exposes them as Swift declarations. This enables an incredible developer experience with automatic translations of C declarations to Swift declarations and autocompletion of imported C code in Xcode.</p> <p>In this post we explored the bare bones way of interoperability of Swift and C without a sophisticated build system, which can be quite tedious. We did so for better understanding of what's going on on the lower level. Usually, we don't have to deal with the compiler directly and we use a more sophisticated build system, most commonly the ones from Xcode and SPM. We also explored the most basic C declaration and haven't covered more advanced types and how they map into Swift.</p> <p>Part 2 of this blog series will try to explain:</p> <ul> <li>how we can leverage SPM to build mixed language targets</li> <li>more advanced C integrations</li> <li>how to go the other way around - calling Swift code from C.</li> </ul> <p>Until next time, M.</p> SKTestSession testing failures 2025-01-10T00:00:00Z https://supersonicbyte.com/blog/sktestsession-set-failure/ <p>With iOS 17, Apple shipped a new API allows us to simulate errors when unit testing StoreKit. The API exposes a method called <a href="https://www.google.com/url?sa=t&amp;source=web&amp;rct=j&amp;opi=89978449&amp;url=https://developer.apple.com/documentation/storekittest/sktestsession/setsimulatederror(_:forapi:)&amp;ved=2ahUKEwiKqOafr-uKAxVt_rsIHaM9IP0QFnoECBoQAQ&amp;usg=AOvVaw3XkW13whJ8Lh3MzSQ47Y9a"><strong>setSimulatedError<API>(_ error: API.Failure?, forAPI api: API</API></strong>)</a> which is intended to be used on a instance of a <strong>SKTestSession</strong>.</p> <p>For example, we would use the API to simulate a <strong>NetworkError</strong> for the purchase API.</p> <pre class="language-swift" tabindex="0"><code class="language-swift"><span class="token keyword">try</span> <span class="token keyword">await</span> session<span class="token punctuation">.</span><span class="token function">setSimulatedError</span><span class="token punctuation">(</span><span class="token punctuation">.</span><span class="token function">generic</span><span class="token punctuation">(</span><span class="token punctuation">.</span><span class="token function">networkError</span><span class="token punctuation">(</span><span class="token class-name">URLError</span><span class="token punctuation">(</span><span class="token punctuation">.</span>badURL<span class="token punctuation">,</span> userInfo<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">:</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span> forAPI<span class="token punctuation">:</span> <span class="token punctuation">.</span>purchase<span class="token punctuation">)</span></code></pre> <p>This is a great addition and it allows us to test possible error handling flows we have in our code. One thing I encountered when working with this API is that the simulated errors would persist even if I called</p> <pre class="language-swift" tabindex="0"><code class="language-swift"> <span class="token keyword">try</span> <span class="token keyword">await</span> session<span class="token punctuation">.</span><span class="token function">setSimulatedError</span><span class="token punctuation">(</span><span class="token nil constant">nil</span><span class="token punctuation">,</span> forAPI<span class="token punctuation">:</span> <span class="token punctuation">.</span>purchase<span class="token punctuation">)</span></code></pre> <p>as outlined in the <a href="https://www.google.com/url?sa=t&amp;source=web&amp;rct=j&amp;opi=89978449&amp;url=https://developer.apple.com/videos/play/wwdc2023/10140/&amp;ved=2ahUKEwiKqOafr-uKAxVt_rsIHaM9IP0QtwJ6BAgNEAI&amp;usg=AOvVaw25IxmWUDO2d3mEb7zz22ls">WWDC session</a>.</p> <p>The current workaround for this is to call the <code>resetToDefaultState</code> method on the session before each test executes.</p> <pre class="language-swift" tabindex="0"><code class="language-swift">session<span class="token punctuation">.</span><span class="token function">resetToDefaultState</span><span class="token punctuation">(</span><span class="token punctuation">)</span></code></pre> <p>Until next time, M.</p> Attaching debugger to system apps 2024-09-19T00:00:00Z https://supersonicbyte.com/blog/attaching-debugger-to-system-apps/ <p>Recently, I was writing some UI code, and a question struck my mind: How did Apple do it? I needed a way to find out what Apple was doing to achieve some of their UI in system apps. Luckily, it turns out we can debug the view hierarchy of system apps on the simulator.</p> <p>In order to do so, we must first turn off <strong>System Integrity Protection (SIP)</strong>. SIP brings enormous efforts to protect your system from unauthorized code. I highly suggest turning it back on as soon as you finish your debugging session. A guide on how to turn it off/on can be found <a href="https://developer.apple.com/documentation/security/disabling-and-enabling-system-integrity-protection">here</a>.</p> <p>After turning SIP off, we can continue. Open up your terminal and run the simulator with the following command:</p> <pre><code>open -a simulator </code></pre> <p>That should launch the simulator. Now, let's type in the following:</p> <pre><code>xcrun simctl listapps booted </code></pre> <p>We utilize the <code>simctl</code> CLI to list all apps on the booted simulator. The printed text contains identifiers of the apps that are on the simulator. After a bit of scrolling, you should see the following identifier::</p> <pre><code> &quot;com.apple.mobileslideshow&quot; = { ApplicationType = System; Bundle = &quot;file:///Library/Developer/C ... </code></pre> <p>This is the identifier of the Photos app on the simulator. Now, let's run the following command:</p> <pre><code>xcrun simctl launch booted com.apple.mobileslideshow </code></pre> <p>This will open up the Photos app on the simulator, and it will print out a number. That number is the PID (process ID) of the freshly launched Photos app. Copy that PID and open up any project in Xcode. Then go to <em>Debug</em> -&gt; <em>Attach to Process by PID or Name</em>, paste in the PID you copied, and hit Attach. Give it a minute, and voilà! You are attached via LLDB to the Photos app. You can even click on the Debug View Hierarchy button, and you'll see the view hierarchy!</p> <p align="center"> <picture><source type="image/avif" srcset="https://supersonicbyte.com/blog/attaching-debugger-to-system-apps/zv0T169iAS-3024.avif 3024w"><source type="image/webp" srcset="https://supersonicbyte.com/blog/attaching-debugger-to-system-apps/zv0T169iAS-3024.webp 3024w"><img loading="lazy" decoding="async" src="https://supersonicbyte.com/blog/attaching-debugger-to-system-apps/zv0T169iAS-3024.png" alt="Image of XCode Visual Debugger showcasing the Photos app." width="3024" height="1802"></picture> </p> <p>Until next time!</p> <p>P.S. Don't forget to turn on SIP.</p> Running code when App is Ready 2024-05-20T00:00:00Z https://supersonicbyte.com/blog/running-code-when-app-is-ready/ <p>Every now and then, you find yourself in a situation where you need to run specific code when certain conditions are met. My recent encounter with this involved the following flow:</p> <p>• A VoIP notification wakes up my app</p> <p>• I need to handle internal call logic and start a call</p> <p>The problem was that the second part of this flow couldn't start until the UI was ready. In my case, I had to ensure that the Coordinators had executed and the UI was in a state ready to handle the call logic. When developing new features, I often explore open-source codebases for inspiration, and this instance was no exception. In the source code of the popular messaging app <code>Signal</code>, I found a class named <a href="https://github.com/signalapp/Signal-iOS/blob/main/SignalServiceKit/Util/AppReadiness.swift">AppReadiness</a> that allows you to queue some code and execute it once the conditions are met. Although the class is somewhat complex and robust, I extracted the main concepts and created a lightweight variant suitable for my use case.</p> <p>Let's explore my lightweight version of <code>AppReadiness</code>.</p> <pre class="language-swift" tabindex="0"><code class="language-swift"><span class="token keyword">final</span> <span class="token keyword">class</span> <span class="token class-name">AppReadiness</span><span class="token punctuation">:</span> <span class="token attribute atrule">@unchecked</span> <span class="token class-name">Sendable</span> <span class="token punctuation">{</span> <span class="token keyword">typealias</span> <span class="token class-name">Task</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token class-name">Void</span> <span class="token keyword">private</span> <span class="token keyword">let</span> queue <span class="token operator">=</span> <span class="token class-name">DispatchQueue</span><span class="token punctuation">(</span>label<span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">"com.supersonicbyte.AppReadiness"</span></span><span class="token punctuation">)</span> <span class="token keyword">private</span> <span class="token keyword">var</span> taskQueue<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token class-name">Task</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token keyword">private</span> <span class="token keyword">var</span> _isAppReady <span class="token operator">=</span> <span class="token boolean">false</span> <span class="token keyword">static</span> <span class="token keyword">let</span> shared <span class="token operator">=</span> <span class="token class-name">AppReadiness</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">private</span> <span class="token keyword">init</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function-definition function">setAppIsReady</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> queue<span class="token punctuation">.</span>sync <span class="token punctuation">{</span> <span class="token keyword">guard</span> <span class="token operator">!</span>_isAppReady <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">}</span> <span class="token function">runQueuedTasks</span><span class="token punctuation">(</span><span class="token punctuation">)</span> _isAppReady <span class="token operator">=</span> <span class="token boolean">true</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function-definition function">isAppReady</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token class-name">Bool</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> queue<span class="token punctuation">.</span>sync <span class="token punctuation">{</span> <span class="token keyword">return</span> _isAppReady <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function-definition function">runSyncNowOrWhenAppBecomesReady</span><span class="token punctuation">(</span><span class="token omit keyword">_</span> task<span class="token punctuation">:</span> <span class="token attribute atrule">@escaping</span> <span class="token class-name">Task</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> queue<span class="token punctuation">.</span>sync <span class="token punctuation">{</span> <span class="token keyword">if</span> _isAppReady <span class="token punctuation">{</span> <span class="token function">task</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> taskQueue<span class="token punctuation">.</span><span class="token function">append</span><span class="token punctuation">(</span>task<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">private</span> <span class="token keyword">func</span> <span class="token function-definition function">runQueuedTasks</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">for</span> task <span class="token keyword">in</span> taskQueue <span class="token punctuation">{</span> <span class="token function">task</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> taskQueue<span class="token punctuation">.</span><span class="token function">removeAll</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre> <p>First, we see that <code>AppReadiness</code> is a singleton, interacting only through the <code>shared</code> instance. This makes sense since the app itself is a single entity, and we need a single source of truth to determine if the app is ready.</p> <p>Next, we have three public methods: <code>setAppIsReady()</code>, <code>isAppReady()</code>, and <code>runSyncNowOrWhenAppBecomesReady(_ task: @escaping Task)</code>.</p> <p>The first method, <code>setAppIsReady()</code>, allows us to mark the app as ready. The goal is to call this method when all the necessary conditions have been met. In my case, I call <code>setAppIsReady()</code> upon the initialization of the last coordinator during app startup.</p> <p>The <code>isAppReady()</code> method allows us to query the AppReadiness status in a thread-safe manner.</p> <p>Finally, the <code>runSyncNowOrWhenAppBecomesReady</code> method allows us to queue code to run when the app is ready. If the app is already ready when this method is called, the task will execute immediately. If the app is not ready, the task will get queued and executed once the <code>setAppIsReady()</code> method is called. Internally, the class uses a <code>DispatchQueue</code> to synchronize access to the shared mutable state, making the class thread-safe.</p> <p>As I mentioned, this is a lightweight solution, leaving plenty of room for potential improvements and additional features based on your needs. For example, if we needed a way to set dependencies between tasks (e.g., running task C only after tasks A and B are completed), we could utilize the <code>NSOperations</code> API.</p> <p>At this <a href="https://github.com/supersonicbyte/BlogCodes/tree/main/AppReadiness">repo</a> you can find a demo of the <code>AppReadiness</code>.</p> <p>Until next time, M.</p> Shortcuts URL Scheme Guide 2024-05-14T00:00:00Z https://supersonicbyte.com/blog/shortcuts-url-scheme/ <p>Many products have great features that aren't utilized as much as they should be. When it comes to iOS, Shortcuts are definitely one of them. To me, Shortcuts provide a meeting place for all the apps the system provides, enabling us to make different apps cooperate. We can run them and connect the outputs of one app to the inputs of others. The possibilities are infinite. This post will focus on one aspect of Shortcuts, and that's the URL scheme. While developers mostly use frameworks like AppIntent and SiriKit to expose app functionalities to Shortcuts, one might ask: how do we run shortcuts from code? The answer is good old URL schemes. So, let's dive into it.</p> <h2 id="opening-a-shortcut">Opening a Shortcut</h2> <p>To open a shortcut, we utilize the following URL: <code>shortcuts://open-shortcut?name=[name]</code>, where instead of the [name] placeholder, we put the name of the shortcut we want to open. So, the code may look like:</p> <pre class="language-swift" tabindex="0"><code class="language-swift"><span class="token keyword">let</span> url <span class="token operator">=</span> <span class="token function">URL</span><span class="token punctuation">(</span>string<span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">"shortcuts://open-shortcut?name=Shazam shortcut"</span></span><span class="token punctuation">)</span><span class="token operator">!</span> <span class="token class-name">UIApplication</span><span class="token punctuation">.</span>shared<span class="token punctuation">.</span><span class="token keyword">open</span><span class="token punctuation">(</span>url<span class="token punctuation">)</span></code></pre> <p>&quot;Shazam shortcut&quot; is a preinstalled shortcut.</p> <h2 id="creating-a-new-shortcut">Creating a New Shortcut</h2> <p>To create a new shortcut, we use the following URL: <code>shortcuts://create-shortcut</code>.</p> <pre class="language-swift" tabindex="0"><code class="language-swift"><span class="token keyword">let</span> url <span class="token operator">=</span> <span class="token function">URL</span><span class="token punctuation">(</span>string<span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">"shortcuts://create-shortcut"</span></span><span class="token punctuation">)</span><span class="token operator">!</span> <span class="token class-name">UIApplication</span><span class="token punctuation">.</span>shared<span class="token punctuation">.</span><span class="token keyword">open</span><span class="token punctuation">(</span>url<span class="token punctuation">)</span></code></pre> <h2 id="opening-the-shortcuts-app">Opening the Shortcuts App</h2> <p>If we just want to open the Shortcuts app, we can use the scheme shortcuts://.</p> <pre class="language-swift" tabindex="0"><code class="language-swift"><span class="token keyword">let</span> url <span class="token operator">=</span> <span class="token function">URL</span><span class="token punctuation">(</span>string<span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">"shortcuts://"</span></span><span class="token punctuation">)</span><span class="token operator">!</span> <span class="token class-name">UIApplication</span><span class="token punctuation">.</span>shared<span class="token punctuation">.</span><span class="token keyword">open</span><span class="token punctuation">(</span>url<span class="token punctuation">)</span></code></pre> <h2 id="running-a-shortcut">Running a Shortcut</h2> <p>For running a shortcut from code, we can use the following URL: <code>shortcuts://run-shortcut?name=[name]&amp;input=[input]&amp;text=[text]</code>. Same as the previous URL, the name parameter represents the name of the shortcut we want to run. The input parameter is optional, providing us with a way to send input to the shortcut. The value of the input can be text, in which case we provide an additional parameter text which will hold the text input for the shortcut, or it can have a value clipboard. In the former case, the input to the shortcut will be collected from the clipboard. I created a shortcut named <code>Note With Input</code> you can find it <a href="https://www.icloud.com/shortcuts/62802e396f014e96b4834bda7f666820">here</a>. To run it from code, we would use:</p> <pre class="language-swift" tabindex="0"><code class="language-swift"><span class="token comment">// Input from text</span> <span class="token keyword">let</span> url <span class="token operator">=</span> <span class="token function">URL</span><span class="token punctuation">(</span>string<span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">"shortcuts://run-shortcut?name=Note With Input&amp;input=text&amp;text=This is a test from text"</span></span><span class="token punctuation">)</span><span class="token operator">!</span> <span class="token class-name">UIApplication</span><span class="token punctuation">.</span>shared<span class="token punctuation">.</span><span class="token keyword">open</span><span class="token punctuation">(</span>url<span class="token punctuation">)</span></code></pre> <pre class="language-swift" tabindex="0"><code class="language-swift"><span class="token comment">// Input from clipboard</span> <span class="token keyword">let</span> url2 <span class="token operator">=</span> <span class="token function">URL</span><span class="token punctuation">(</span>string<span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">"shortcuts://run-shortcut?name=Note With Input&amp;input=clipboard"</span></span><span class="token punctuation">)</span><span class="token operator">!</span> <span class="token class-name">UIApplication</span><span class="token punctuation">.</span>shared<span class="token punctuation">.</span><span class="token keyword">open</span><span class="token punctuation">(</span>url2<span class="token punctuation">)</span></code></pre> <h2 id="returning-to-the-app-after-the-shortcut-is-finished">Returning to the App after the Shortcut is Finished</h2> <p>To return to the app (call site) from which the shortcut was triggered, we utilize <code>x-callback-url</code>. The <code>x-callback-url</code> standard comes with the following parameters which can be provided in the URL:</p> <p>• <code>x-success</code> (optional): A URL which opens when a shortcut is finished running. With this, a parameter named result is appended to the URL, containing the textual output of the shortcut. • <code>x-cancel</code> (optional): A URL that opens when the shortcut is cancelled by the user. • <code>x-error</code> (optional): A URL that opens when the shortcut fails because of an error. A parameter named <code>errorMessage</code> is appended to the URL, containing the description of the error.</p> <p>So, if we wanted to return to the app after running the previous <code>Note With Input</code> action, the URL would look like: <code>shortcuts://x-callback-url/run-shortcut?name=Add Note&amp;input=text&amp;text=Test&amp;x-success=shortcutsdemo://</code>.</p> <p>In the <code>x-success</code> parameter, we put the URL scheme of our app. So instead of <code>shortcutsdemo://</code>, you would put your app's scheme.</p> <h2 id="returning-output-from-the-shortcut-to-the-app">Returning Output from the Shortcut to the App</h2> <p>In the previous section, we mentioned that a result parameter is appended to the <code>x-success</code> callback URL when the shortcut finishes and has a textual output. In order for the result parameter to be present, we need the shortcut to output a result. I created <a href="https://www.icloud.com/shortcuts/34b404316851486b81120d8adf073108">this</a> shortcut, which outputs all created shortcuts.</p> <pre class="language-swift" tabindex="0"><code class="language-swift"><span class="token keyword">let</span> url <span class="token operator">=</span> <span class="token function">URL</span><span class="token punctuation">(</span>string<span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">"shortcuts://x-callback-url/run-shortcut?name=Get My Shortcuts&amp;x-success=shortcutsdemo://"</span></span><span class="token punctuation">)</span><span class="token operator">!</span> <span class="token class-name">UIApplication</span><span class="token punctuation">.</span>shared<span class="token punctuation">.</span><span class="token keyword">open</span><span class="token punctuation">(</span>url<span class="token punctuation">)</span></code></pre> <p>Now, in order to get the result, we add the following code to our ContentView:</p> <pre class="language-swift" tabindex="0"><code class="language-swift"><span class="token punctuation">.</span>onOpenURL <span class="token punctuation">{</span> url <span class="token keyword">in</span> <span class="token keyword">if</span> url<span class="token punctuation">.</span>scheme <span class="token operator">==</span> <span class="token string-literal"><span class="token string">"shortcutsdemo"</span></span> <span class="token punctuation">{</span> <span class="token keyword">guard</span> <span class="token keyword">let</span> dirty <span class="token operator">=</span> url<span class="token punctuation">.</span>absoluteString<span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span>separator<span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">"result="</span></span><span class="token punctuation">)</span><span class="token punctuation">.</span>last<span class="token punctuation">,</span> <span class="token keyword">let</span> clean <span class="token operator">=</span> <span class="token class-name">String</span><span class="token punctuation">(</span>dirty<span class="token punctuation">)</span><span class="token punctuation">.</span>removingPercentEncoding <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">}</span> <span class="token keyword">let</span> shortcuts <span class="token operator">=</span> clean<span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span>separator<span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">"\n"</span></span><span class="token punctuation">)</span><span class="token punctuation">.</span>map <span class="token punctuation">{</span> <span class="token class-name">String</span><span class="token punctuation">(</span><span class="token short-argument">$0</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token function">print</span><span class="token punctuation">(</span>shortcuts<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre> <p>The code above reacts to URLs that correspond to our app's scheme, then we extract the data from the URL.</p> <p>I created a sample app, so you can check it out <a href="https://github.com/supersonicbyte/BlogCodes/tree/main/ShortcutsDemo">here</a>.</p> Introducing Publishmon 2024-05-05T00:00:00Z https://supersonicbyte.com/blog/introducing-publishmon/ <p>Recently, I made the decision to rewrite my personal site/blog using <a href="https://github.com/JohnSundell/Publish"><code>Publish</code></a>. Publish is a popular choice for iOS developers and Swift enthusiasts, and I quite enjoyed the learning curve. However, one thing bothered me: I hated the fact that I had to manually regenerate files and run <code>publish run</code> in the terminal to restart the HTTP server. I remembered when I was writing some Node.js code that there was a package called <code>nodemon</code>, which would restart the server every time a file changed. I searched for a similar solution for Publish but didn't find any. So, the idea of <code>Publishmon</code> was born!</p> <p>I tinkered with some <code>FSEventStreamCreate</code> APIs and managed to make the site regeneration flow work pretty nicely with Publish. Behind the scenes, Publishmon runs publish run for you every time one of your source files changes. It's available as a Swift package on <a href="https://github.com/supersonicbyte/Publishmon">this repository</a>.</p> <p>Until next time!</p> Xcdatamodeld No File Inspector 2023-09-22T00:00:00Z https://supersonicbyte.com/blog/xcdatamodeld-no-file-inspector/ <p>Recently I worked on a project that uses Core Data. The underlying model changed and I needed to update an entity. This involved changing an attribute from <code>String?</code> to <code>[String]?</code>. In order to make the migration I added a new version of the model. When I tried to set the current active model in the <code>xcdatamodeld</code> file, the file inspector was blank and there wasn’t a way to do it through the UI (Xcode 14.3.1). So, I tried opening the <code>xcdatamoded</code> file in Finder as a text file and modifying the <code>_XCCurrentVersionName</code> key in the XML, but that didn’t work either. The green checkmark still stayed on the old model. I ran <code>git diff</code> in my terminal and saw that the <code>pbxproj</code> file was modified. After some digging around I saw a <code>XCVersionGroup</code> in the pbxproj file, which looked like this:</p> <pre><code>/* Begin XCVersionGroup section */ 724C37322ABD9D030026634A /* Model.xcdatamodeld */ = { isa = XCVersionGroup; children = ( 724C37332ABD9D030026634A /* Model.xcdatamodel */, 724C37342ABD9D030026634A /* Model 2.xcdatamodel */, ); currentVersion = 724C37332ABD9D030026634A /* Model.xcdatamodel */; path = Cocoon.xcdatamodeld; sourceTree = &quot;&lt;group&gt;&quot;; versionGroupType = wrapper.xcdatamodel; }; /* End XCVersionGroup section */ </code></pre> <p>I updated the currentVersion to point to the Model 2 identifier. So it looked like this after the change:</p> <pre><code>/* Begin XCVersionGroup section */ 724C37322ABD9D030026634A /* Model.xcdatamodeld */ = { isa = XCVersionGroup; children = ( 724C37332ABD9D030026634A /* Model.xcdatamodel */, 724C37342ABD9D030026634A /* Model 2.xcdatamodel */, ); currentVersion = 724C37342ABD9D030026634A /* Model 2.xcdatamodel &lt;-- THIS CHANGED&gt; */; path = Cocoon.xcdatamodeld; sourceTree = &quot;&lt;group&gt;&quot;; versionGroupType = wrapper.xcdatamodel; }; /* End XCVersionGroup section */ </code></pre> <p>I restared Xcode and the Model 2 was selected as the active model version!</p>