<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>Recoursive</title>
		<description>My name is Robin Kunde. I am a software engineering consultant currently specializing in iOS mobile development and the Swift compiler. I have worked on large e-commerce projects and mobile apps for major retailers in the past.</description>
		<link>https://recoursive.com</link>
		<atom:link href="https://recoursive.com/feed.xml" rel="self" type="application/rss+xml" />
		
			<item>
				<title>SimPasteboardHelper</title>
				<description>&lt;p&gt;If you’ve been forced to run Xcode through Rosetta (for example, because a binary framework vendor has been extraordinarily slow to adopt the xcframework format or simply isn’t including the right simulator slice), you may have noticed that copy and pasting inside the simulated app isn’t working. My knowledge of macOS isn’t deep enough to say why exactly that is, but the error messages in the debug console point to some sort of XPC/permissions issue. The copied contents are written to a disk cache, but can’t be read back for some reason. I assume it has something to do with the simulator itself running natively, while the app runs through Rosetta.&lt;/p&gt;

&lt;p&gt;Regardless, for those of us who suffer from this issue or otherwise find the pasteboard sync between macOS and the simulator unreliable, I developed a small helper class you can drop into your project. It’s designed to be excluded automatically from release builds and not activate outside the simulator.&lt;/p&gt;

&lt;p&gt;Simulated apps aren’t meaningfully isolated&lt;sup id=&quot;fnref:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; from the host system, which allows an app to monitor files outside its container. The helper by default creates an empty file inside your home directory and makes any text you save inside this file available for pasting inside the app with a special keyboard shortcut.&lt;/p&gt;

&lt;p&gt;There’s a few enhancements I can think of, such as syncing automatically by running an agent that monitors the host system pasteboard. It should also be possible to make data move in the other direction, from the simulated app to the monitored file. However, the helper covers the most common use case for now.&lt;/p&gt;

&lt;p&gt;I hope you find it useful.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/robinkunde/SimPasteboardHelper&quot;&gt;SimPasteboardHelper on Github&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;This can be a confounding factor with build issues where a dynamic framework isn’t copied into the app binary. It will still work in the simulator because the app can load the framework from the derived data directory, but will crash when running on device. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
				<pubDate>Mon, 05 Sep 2022 00:00:00 -0400</pubDate>
				<link>https://recoursive.com/2022/09/05/simpasteboardhelper/</link>
				<guid isPermaLink="true">https://recoursive.com/2022/09/05/simpasteboardhelper/</guid>
			</item>
		
			<item>
				<title>Form validation is hard</title>
				<description>&lt;p&gt;Here’s a fun bug I encountered in Apple Wallet. I invited someone as a participant for my Apple Card (essentially an authorized user). The processes for both sending this invite and accepting it are pretty slick. However, whenever they tried to accept the invite, they were greeted with this error message at the address validation step.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2022-09-02-apple_wallet_form_validation_bug.png&quot; alt=&quot;A popup saying Apple Card Unavailable&quot; /&gt;&lt;/p&gt;

&lt;p&gt;A few notes:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;The error message sucks. This is sadly not unusual. Apple will often limit themselves to generic messages like this. They also don’t like using error codes, which would make researching issues and talking to support much easier.&lt;/li&gt;
  &lt;li&gt;The message mentions both Apple Card and Apple Pay, the latter of which doesn’t really seem related here, but I assume everything to do with Apple Card just goes through the Apple Pay service layer.&lt;/li&gt;
  &lt;li&gt;“Apple Pay services are currently unavailable” seems to point to a backend or network problem of some sort.&lt;/li&gt;
  &lt;li&gt;The message pops up almost immediately after hitting “Next”, which is unusual if it’s really a communication issue. Apple’s backend services are (in my experience at least) very slow, and their clients have high timeout values and are set to retry before failing.&lt;/li&gt;
  &lt;li&gt;Unrelated to the issue at hand: The contents of the state field and the UIPickerView attached to it are missing from screenshots and screen recordings. Curious.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After trying a few times throughout the day, I contacted Apple Card support, which is really the support team at Goldman Sachs. This experience was mildly infuriating because even though you use Apple’s Business Chat to talk to them, the chat session times out after about 10-15m if you don’t respond and then you have to start over from the beginning.
They have me verify that both phones are on the latest version of iOS, before determining that it must be an issue on Apple’s side and starting a new chat session with Apple’s support team. The chat history is not transferred, so I have to repeat some context. In addition to verifying the iOS version and the country we both reside in, they also ask for device serial numbers. I’m guessing this is to check that devices haven’t been jailbroken or banned from Apple services. After asking me to try accepting the invite on cellular instead of Wi-Fi and having me send them a screen recording of the issue, they finally determine that I need to speak to a support specialist over the phone. I set up a time when I can be hands-on with the other iPhone.&lt;/p&gt;

&lt;p&gt;After the phone call begins, we don’t get very far into the debugging steps before I notice something. When the address validation form is first displayed, it is pre-populated with what Apple or Goldman Sachs think your current address is. But the second address field isn’t displaying a placeholder even though it’s empty. And indeed, when I hit backspace, it removes a space or other blank unicode character! I try hitting submit and it goes through!&lt;/p&gt;

&lt;p&gt;The form had been prefilled with information that it wasn’t designed to accept. Impossible to say from the outside how this happened, so let’s just say form validation is hard, no matter who you are.&lt;/p&gt;
</description>
				<pubDate>Fri, 02 Sep 2022 00:00:00 -0400</pubDate>
				<link>https://recoursive.com/2022/09/02/form_validation_is_hard/</link>
				<guid isPermaLink="true">https://recoursive.com/2022/09/02/form_validation_is_hard/</guid>
			</item>
		
			<item>
				<title>How-to: Create compile time reminders in Xcode</title>
				<description>&lt;p&gt;Workarounds are a fact of life for programmers. You rarely have the ability to fix all the buggy code you have to work with, and even if you do, it might not be worth the time and effort to find and validate a proper fix. But while a workaround allows you to move forward quickly, it also creates a maintenance burden. Creative solutions like this are usually more fragile and will need to be revisited occasionally. Here are a few ways you can use the compiler to help remind you.&lt;/p&gt;

&lt;h3 id=&quot;swift-deprecation-attribute&quot;&gt;Swift Deprecation Attribute&lt;/h3&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;@available&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;swift&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;deprecated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;5.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Verify if workarounds are still needed&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This attribute will produce a warning if the selected Swift version is available in the version of Xcode you’re using. For 5.5 for example, this would generate a warning in Xcode 13.0 but not Xcode 12.5.
Downside: The warning is generate at the callsite, so there would be no warning if the method isn’t actively used, due to refactoring for example.&lt;/p&gt;

&lt;h3 id=&quot;ios-deprecation-attribute&quot;&gt;iOS Deprecation Attribute&lt;/h3&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;@available&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iOS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;deprecated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;14.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Verify if workarounds are still needed&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Looks similar to the Swift version above, but works &lt;em&gt;very differently&lt;/em&gt;. This attribute will produce a warning if the selected iOS (or tvOS, or macOS) version is equal to or below your &lt;em&gt;deployment target&lt;/em&gt;. In other words, it will not be triggered by changing the Xcode version you’re using, but after you remove support for older operating systems. For the above example, no warning will be generated until your deployment target is iOS 14 or higher.
The same caveat about callsites applies.&lt;/p&gt;

&lt;h3 id=&quot;conditional-compilation-block&quot;&gt;Conditional Compilation Block&lt;/h3&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;cp&quot;&gt;#if compiler(&amp;gt;=5.5)&lt;/span&gt;
    &lt;span class=&quot;cp&quot;&gt;#warning(&quot;Verify if workarounds are still needed&quot;)&lt;/span&gt;
    &lt;span class=&quot;cp&quot;&gt;#endif    &lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;By using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#warning&lt;/code&gt; statement, you can generate a compiler warning directly at the line you want to draw attention to, regardless of whether the enclosing method is actually used. By wrapping it in this condtional compilation check, you can get Xcode to ignore the statement until you’re using an Xcode version that ships with the given compiler version.&lt;/p&gt;

&lt;p&gt;Alternatively, you can check for the Swift version specified in your build settings like this:&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;cp&quot;&gt;#if swift(&amp;gt;=5.5)&lt;/span&gt;
    &lt;span class=&quot;cp&quot;&gt;#warning(&quot;Verify if workarounds are still needed&quot;)&lt;/span&gt;
    &lt;span class=&quot;cp&quot;&gt;#endif    &lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;build-phase-script&quot;&gt;Build Phase Script&lt;/h3&gt;

&lt;p&gt;If you want to check for the latest iOS SDK the compiler supports, or really any other arbitrary condition, you can write a build phase script that emits a warning (or error). Optionally, you can even emit filenames and line numbers to make the warnings more actionable.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$SDK_VERSION&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;14.5&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;warning: Verify if workarounds are still needed&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can see what envrionment variables the build system emits by exporting the full build log from the Report Navigator.&lt;/p&gt;

&lt;p&gt;Make sure you uncheck “Based on dependency analysis” if the script checks more complex options and needs to run every time you build.
&lt;img src=&quot;/images/build_script_reminder.png&quot; alt=&quot;A screenshot of an example smart banner&quot; /&gt;&lt;/p&gt;
</description>
				<pubDate>Tue, 14 Sep 2021 00:00:00 -0400</pubDate>
				<link>https://recoursive.com/2021/09/14/create_compile_time_reminders/</link>
				<guid isPermaLink="true">https://recoursive.com/2021/09/14/create_compile_time_reminders/</guid>
			</item>
		
			<item>
				<title>How-to: Create a zip file on iOS using Swift without 3rd party dependencies</title>
				<description>&lt;p&gt;Getting the files your app stores on the device back out so you can look at them for debugging or similar is not as easy as it could be. There’s a number of filebrowser libraries out there, but it’s a bit overkill or occasional use and probrably not something you want to ship with your app either. Here’s how to create a zip file from any directory and move it off the device using the share sheet (UIActivityViewController).&lt;/p&gt;

&lt;p&gt;I wrote this in a hurry, so there’s lots of force unwrapping. Fine for development, but if you intend to ship this, make sure you deal with potential errors properly.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-swift&quot; data-lang=&quot;swift&quot;&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;fm&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;FileManager&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;baseDirectoryUrl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;urls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;documentDirectory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;userDomainMask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// populate the app&apos;s documents directory with some example files&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;fileUrl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;baseDirectoryUrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;appendingPathComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.txt&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;atomically&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;encoding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;utf8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// this will hold the URL of the zip file&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;archiveUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// if we encounter an error, store it here&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NSError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;coordinator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NSFileCoordinator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// zip up the documents directory&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// this method is synchronous and the block will be executed before it returns&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// if the method fails, the block will not be executed though&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// if you expect the archiving process to take long, execute it on another queue&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;coordinator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;coordinate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;readingItemAt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;baseDirectoryUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;forUploading&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;zipUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// zipUrl points to the zip file created by the coordinator&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// zipUrl is valid only until the end of this block, so we move the file to a temporary folder&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;tmpUrl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;try!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;itemReplacementDirectory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;userDomainMask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;appropriateFor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zipUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;appendingPathComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;archive.zip&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;moveItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;at&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zipUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tmpUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// store the URL so we can use it outside the block&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;archiveUrl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tmpUrl&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;archiveUrl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;archiveUrl&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// bring up the share sheet so we can send the archive with AirDrop for example&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;avc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UIActivityViewController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;activityItems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;archiveUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;applicationActivities&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;avc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;animated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</description>
				<pubDate>Thu, 25 Feb 2021 00:00:00 -0500</pubDate>
				<link>https://recoursive.com/2021/02/25/create_zip_archive_using_only_foundation/</link>
				<guid isPermaLink="true">https://recoursive.com/2021/02/25/create_zip_archive_using_only_foundation/</guid>
			</item>
		
			<item>
				<title>Reference: Obscure UserDefaults That Affect AppKit Behavior</title>
				<description>&lt;p&gt;Setting these UserDefaults in your app’s domain will change AppKit behavior at runtime.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;NSDisplayCycleLoggingEnabled&lt;/strong&gt; (h/t &lt;a href=&quot;https://twitter.com/pilky/status/1259201481015726082&quot;&gt;@pilky&lt;/a&gt;)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;NSTextViewAvoidLayoutWhileDrawing&lt;/strong&gt;: Disables a fastpath that allows layout passes to happen while drawRect is being invoked. (h/t &lt;a href=&quot;https://twitter.com/pilky/status/1259202092801064960&quot;&gt;@pilky&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
</description>
				<pubDate>Fri, 16 Oct 2020 00:00:00 -0400</pubDate>
				<link>https://recoursive.com/2020/10/16/obscure_userdefaults_appkit/</link>
				<guid isPermaLink="true">https://recoursive.com/2020/10/16/obscure_userdefaults_appkit/</guid>
			</item>
		
			<item>
				<title>How-to: Disable automatically organizing files on disk after move in Xcode project</title>
				<description>&lt;p&gt;Starting with Xcode 9 or so, Xcode by default keeps the on-disk layout of a project in sync with what you see in the Project Navigator. So when you move a file from one group to another, it will be moved between the respective folders on disk as well.&lt;/p&gt;

&lt;p&gt;You can explicitly disable this behavior for a given group in a few different ways:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Select “New Group without Folder” when creating a new group&lt;/li&gt;
  &lt;li&gt;Remove the folder association from the group by clicking the (x) icon in the File inspector &lt;img src=&quot;/images/2020-09-28-file_inspector.png&quot; alt=&quot;A screenshot of the file inspector&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;Disable it globally by setting a user default on the command line
    &lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;defaults write com.apple.dt.Xcode IDEDisableStructureEditingCoordinator &lt;span class=&quot;nt&quot;&gt;-bool&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</description>
				<pubDate>Mon, 28 Sep 2020 00:00:00 -0400</pubDate>
				<link>https://recoursive.com/2020/09/28/disable_structure_editing_coordinator/</link>
				<guid isPermaLink="true">https://recoursive.com/2020/09/28/disable_structure_editing_coordinator/</guid>
			</item>
		
			<item>
				<title>How-to: Reset Privacy Permissions in macOS</title>
				<description>&lt;p&gt;&lt;strong&gt;UPDATED 2023-02-12. This how-to is now out of date for macOS Big Sur and newer. I’m leaving it up for archival reasons, since I don’t have to bandwidth to bring it up-to-date at the moment (it requires a lot of reboots and messing with system files). Check out more up-to-date writeups by &lt;a href=&quot;https://www.macworld.com/article/1378183/how-to-reset-macos-privacy-preferences-when-other-options-dont-work.html&quot;&gt;Glenn Fleishman&lt;/a&gt; and &lt;a href=&quot;https://eclecticlight.co/2023/02/09/should-you-reset-its-database-or-delete-it-the-woes-of-tcc/&quot;&gt;Howard Oakley&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPDATED 2022-03-22. I have not tested the TCC.db deletion procedure on Monterey, so the actual steps might differ now.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you’re having issues with the privacy permissions system in macOS Catalina or newer versions, such as being unable to change the settings, there are several steps you can take to remedy the situation.
These settings are currently stored in a SQLite database located at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Library/Application Support/com.apple.TCC/TCC.db&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;tccutil&quot;&gt;tccutil&lt;/h3&gt;

&lt;p&gt;tccutil is a command line tool you can use to reset system privacy permissions for macOS apps. There’s some granularity here. You can reset individual permissions or all permissions, and you can make this change for individual apps or all apps.&lt;/p&gt;

&lt;p&gt;Individual apps are identified through their bundle identifier. To get an app’s bundle ID from its name, you can use either of the following commands:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;lsappinfo info &lt;span class=&quot;nt&quot;&gt;-only&lt;/span&gt; bundleid &lt;span class=&quot;s2&quot;&gt;&quot;Google Chrome&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;osascript &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;id of app &quot;Google Chrome&quot;&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A list of individual permissions is not documented anywhere as far as I know, but you can currently get a rough idea by checking for certain strings in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TCC.framework&lt;/code&gt;. I’ve listed two different commands below as the exact location of the framework has changed between Catalina and Monterey.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;strings /System/Library/PrivateFrameworks/TCC.framework/TCC | fgrep kTCCService | fgrep &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos; &apos;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; s/kTCCService// | &lt;span class=&quot;nb&quot;&gt;sort&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;or&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;strings /System/Library/PrivateFrameworks/TCC.framework/Support/tccd | fgrep kTCCService | fgrep &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos; &apos;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; s/kTCCService// | &lt;span class=&quot;nb&quot;&gt;sort&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;At the time of writing this post, the list contained the following entries:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Accessibility
AddressBook
All
AlwaysAllowedService.AppleEvents
AppleEvents
BluetoothAlways
BluetoothPeripheral
BluetoothWhileInUse
Calendar
Calls
Camera
ContactsFull
ContactsLimited
DeveloperTool
ExposureNotification
ExposureNotificationRegion
FaceID
Facebook
FallDetection
FileProviderDomain
FileProviderPresence
FocusStatus
GameCenterFriends
KeyboardNetwork
LinkedIn
ListenEvent
Liverpool
MSO
MediaLibrary
Microphone
Motion
NearbyInteraction
Photos
PhotosAdd
PostEvent
Prototype3Rights
Prototype4Rights
Reminders
ScreenCapture
SensorKitAmbientLightSensor
SensorKitBedSensing
SensorKitBedSensingWriting
SensorKitDeviceUsage
SensorKitElevation
SensorKitFacialMetrics
SensorKitForegroundAppCategory
SensorKitKeyboardMetrics
SensorKitLocationMetrics
SensorKitMessageUsage
SensorKitMotion
SensorKitMotionHeartRate
SensorKitOdometer
SensorKitPedometer
SensorKitPhoneUsage
SensorKitSoundDetection
SensorKitSpeechMetrics
SensorKitStrideCalibration
SensorKitWatchAmbientLightSensor
SensorKitWatchFallStats
SensorKitWatchForegroundAppCategory
SensorKitWatchHeartRate
SensorKitWatchMotion
SensorKitWatchOnWristState
SensorKitWatchPedometer
SensorKitWatchSpeechMetrics
ShareKit
SinaWeibo
Siri
SpeechRecognition
SystemPolicyAllFiles
SystemPolicyDesktopFolder
SystemPolicyDeveloperFiles
SystemPolicyDocumentsFolder
SystemPolicyDownloadsFolder
SystemPolicyNetworkVolumes
SystemPolicyRemovableVolumes
SystemPolicySysAdminFiles
TencentWeibo
Twitter
Ubiquity
UserAvailability
UserTracking
WebKitIntelligentTrackingPrevention
Willow
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To reset the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Camera&lt;/code&gt; permission for Google Chrome, you would use:&lt;/p&gt;
&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;tccutil reset Camera com.google.Chrome
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To reset the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Camera&lt;/code&gt; permission for all apps, you would use:&lt;/p&gt;
&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;tccutil reset Camera
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To reset the all permissions for Google Chrome, you would use (note: keywords are case-sensitive):&lt;/p&gt;
&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;tccutil reset All com.google.Chrome
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To reset the all permissions for all apps, you would use:&lt;/p&gt;
&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;tccutil reset All
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;reset-pram&quot;&gt;Reset PRAM&lt;/h3&gt;

&lt;p&gt;If the above changes do not take effect immediately, you might have to reset your Mac’s PRAM. Instructions to do so can be found &lt;a href=&quot;https://support.apple.com/en-us/HT204063&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;delete-tcc-database&quot;&gt;Delete TCC database&lt;/h3&gt;

&lt;p&gt;If the changes still do not take effect, you can delete the entire permission database. This requires booting into recovery mode on your &lt;a href=&quot;https://support.apple.com/en-us/HT201314&quot;&gt;Intel&lt;/a&gt; or &lt;a href=&quot;https://support.apple.com/guide/mac-help/macos-recovery-a-mac-apple-silicon-mchl82829c17/mac&quot;&gt;Apple Silicon&lt;/a&gt; Mac, as the file cannot be deleted while the OS is running. Do so at your own risk.&lt;/p&gt;

&lt;p&gt;The steps are:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Boot into reovery mode&lt;/li&gt;
  &lt;li&gt;Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Disk Utility&lt;/code&gt; to mount your system and data partitions. They are listed on the left side under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Internal&lt;/code&gt; and are named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Macintosh HD&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Macintosh HD - Data&lt;/code&gt; by default.&lt;/li&gt;
  &lt;li&gt;Quit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Disk Utility&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Open &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Terminal&lt;/code&gt; through the Utilities menu&lt;/li&gt;
  &lt;li&gt;Delete database:
    &lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; /Volumes/Macintosh&lt;span class=&quot;se&quot;&gt;\ &lt;/span&gt;HD/Library/Application&lt;span class=&quot;se&quot;&gt;\ &lt;/span&gt;Support/com.apple.TCC/TCC.db
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;Note: If renamed your partition from the default name, the path will differ.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;Restart&lt;/li&gt;
&lt;/ol&gt;
</description>
				<pubDate>Tue, 03 Mar 2020 00:00:00 -0500</pubDate>
				<link>https://recoursive.com/2020/03/03/reset_macos_privacy_permissions/</link>
				<guid isPermaLink="true">https://recoursive.com/2020/03/03/reset_macos_privacy_permissions/</guid>
			</item>
		
			<item>
				<title>Reference: Obscure iOS Simulator Setings</title>
				<description>&lt;p&gt;There are a number of iOS simulator settings that are either completely undocumented or only mentioned in passing in release notes, so I’ll try to put them here as I come across them in order to make easier to find. Shoutout to Twitter for surfacing them!&lt;/p&gt;

&lt;h3 id=&quot;disabling-automatic-pasteboard-sync&quot;&gt;Disabling automatic pasteboard sync&lt;/h3&gt;

&lt;p&gt;h/t to &lt;a href=&quot;https://twitter.com/ObjCandTwits/status/1227459913594658816&quot;&gt;@ObjCandTwits&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Automatically syncing the pasteboard between macOS and the simulator is a welcome feature. Unfortunately, at least in my experience, it’s not very realiable and will randomly stop working. Worse, for some people it will cause stalls when running a lot of UI tests. Here’s how you disable it &lt;em&gt;globally&lt;/em&gt;:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;defaults write com.apple.iphonesimulator PasteboardAutomaticSync &lt;span class=&quot;nt&quot;&gt;-bool&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is the user default corresponding to the option in Simulator.app’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Edit&lt;/code&gt; menu, but with the command line snippet, you can set it in your CI pipeline as well.&lt;/p&gt;

&lt;h3 id=&quot;extend-apptest-launch-timeout&quot;&gt;Extend app/test launch timeout&lt;/h3&gt;

&lt;p&gt;Under certain circumstances, starting UI tests can take long enough for the simulator’s watchdog process to kick in a kill the process. In that case, you will see a crash or error with “Termination Reason: Namespace SPRINGBOARD, Code 0x8badf00d”. You can increase the timeout &lt;em&gt;for each simulator instance&lt;/em&gt; like this:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;xcrun simctl spawn &amp;lt;device UUID&amp;gt; defaults write com.apple.springboard FBLaunchWatchdogScale 2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;show-touches-in-simulator&quot;&gt;Show touches in simulator&lt;/h3&gt;

&lt;p&gt;This one is more well known, but figured I’d include it here because of how useful it is. If you ever want to show off something in the simulator over video call, you can use this setting to provide a visual indicator for when you’re clicking or dragging.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;defaults write com.apple.iphonesimulator ShowSingleTouches &lt;span class=&quot;nt&quot;&gt;-bool&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
				<pubDate>Wed, 12 Feb 2020 00:00:00 -0500</pubDate>
				<link>https://recoursive.com/2020/02/12/obscure_simulator_settings/</link>
				<guid isPermaLink="true">https://recoursive.com/2020/02/12/obscure_simulator_settings/</guid>
			</item>
		
			<item>
				<title>JSON validation bug in MariaDB 10.4.8 when Unicode escaping is used</title>
				<description>&lt;p&gt;It finally happened to me. A user entered an emoji into an input field, and a production system broke.&lt;/p&gt;

&lt;p&gt;I don’t want to get into the weeds of the particular system, except to say that it’s a strictly ordered queue that couldn’t move forward because an entry was being rejected by a database validation check.&lt;/p&gt;

&lt;p&gt;Three pieces of background:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;MariaDB implents its JSON column data type by combining the regular LONGTEXT column type with a validation constraint that runs inserted or updated values through &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;json_valid()&lt;/code&gt;. Column definition looks something like this: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;column_name longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(&apos;column_name&apos;))&lt;/code&gt;. I don’t how this differes from MySQL, if at all.&lt;/li&gt;
  &lt;li&gt;The &lt;a href=&quot;http://www.ietf.org/rfc/rfc4627.txt&quot;&gt;JSON RFC&lt;/a&gt; states that only a handful of Unicode characters inside strings have to be escaped, but all of them &lt;em&gt;may&lt;/em&gt; be escaped.
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;2.5.  Strings

[...] All Unicode characters may be placed within the
quotation marks except for the characters that must be escaped:
quotation mark, reverse solidus, and the control characters (U+0000
through U+001F).

Any character may be escaped.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;PHP’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;json_encode()&lt;/code&gt; escapes all Unicode characters by default. (This behavior can be disabled by passing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JSON_UNESCAPED_UNICODE&lt;/code&gt; option)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The issue presented itself as database inserts failing because the JSON validation was failing, but only if the emoji in question (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;😏&lt;/code&gt;) was escaped (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;\ud83d\ude0f&lt;/code&gt;). Running the data manually through &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;json_valid()&lt;/code&gt; succeeded though. This seems to indicate that MariaDB was using different code paths for column validation and the query function.&lt;/p&gt;

&lt;p&gt;We were already running a version of MariaDB that didn’t exhibit this issue (10.4.11) in another environment, so rather than making a bunch of code changes we added some tests to check for this issue and upgrade the production docker images with the new database server version. I checked the changelogs and saw no mention of this issue, though, so it’s possible this was fixed unintentionally and might resurface in a future build. I’ll look into filing a bug for it.&lt;/p&gt;
</description>
				<pubDate>Tue, 21 Jan 2020 00:00:00 -0500</pubDate>
				<link>https://recoursive.com/2020/01/21/mariadb_json_validation_bug/</link>
				<guid isPermaLink="true">https://recoursive.com/2020/01/21/mariadb_json_validation_bug/</guid>
			</item>
		
			<item>
				<title>Reference: Swift Error Catch Syntax</title>
				<description>&lt;p&gt;Below is some example code that covers common patterns for catching specific types of errors. This code is valid for Swift 5.1. I figure this might be useful for some folks since the official documentation doesn’t provide examples for all of these, and few tutorials cover it completely.&lt;/p&gt;

&lt;div class=&quot;language-swift highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;callThatCanThrow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;DecodingError&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keyNotFound&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// for error enums, this will match a specific case&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;DecodingError&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;keyNotFound&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stringValue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;test&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// for error enums, this will match a specific case, unwrap the payload,&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// and execute this block if the where condition is met&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;DecodingError&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;keyNotFound&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// for error enums, this will match a specific case&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// and unwrap the payload&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;DecodingError&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;keyNotFound&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// for error enums, this will match a specific case&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// and unwrap the payload (alternative syntax)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;DecodingError&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// catch all errors of type DecodingError, but don&apos;t bind it to a variable&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;DecodingError&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// catch all errors of type DecodingError, and bind it to `error`&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;NSError&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// catch all errors, but convert covert to NSError before binding&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// AFAIK, all errors can be implictly converted to NSError,&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// so this should always succeed&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// catch everything else, and implicitly bind it to a variable named `error`&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
				<pubDate>Sat, 05 Oct 2019 00:00:00 -0400</pubDate>
				<link>https://recoursive.com/2019/10/05/swift_error_catch_syntax/</link>
				<guid isPermaLink="true">https://recoursive.com/2019/10/05/swift_error_catch_syntax/</guid>
			</item>
		
	</channel>
</rss>
