<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Scott Berrevoets - til</title><link href="https://scottberrevoets.com/" rel="alternate"/><link href="https://scottberrevoets.com/feeds/til.atom.xml" rel="self"/><id>https://scottberrevoets.com/</id><updated>2026-04-24T00:00:00-07:00</updated><entry><title>Objective-C enums can be string-backed... sort of</title><link href="https://scottberrevoets.com/til/objective-c-enums-can-be-string-backed-sort-of/" rel="alternate"/><published>2026-04-24T00:00:00-07:00</published><updated>2026-04-24T00:00:00-07:00</updated><author><name>Scott Berrevoets</name></author><id>tag:scottberrevoets.com,2026-04-24:/til/objective-c-enums-can-be-string-backed-sort-of/</id><summary type="html">&lt;p&gt;While looking at &lt;code&gt;AVAudioSessionErrorCode&lt;/code&gt; in Xcode, I noticed something
interesting in its definition:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;typedef&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CF_ENUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AVAudioInteger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AVAudioSessionErrorCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;AVAudioSessionErrorCodeNone&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;AVAudioSessionErrorCodeMediaServicesFailed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;msrv&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c1"&gt;// 0x6D737276, 1836282486&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;AVAudioSessionErrorCodeIsBusy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;act&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="c1"&gt;// 0x21616374, 560030580&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;AVAudioSessionErrorCodeIncompatibleCategory&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// 0x21636174, 560161140&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;AVAudioSessionErrorCodeCannotInterruptOthers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 0x21696E74, 560557684&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;AVAudioSessionErrorCodeMissingEntitlement&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;ent&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// 0x656E743F, 1701737535&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;AVAudioSessionErrorCodeSiriIsRecording&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;siri&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="c1"&gt;// 0x73697269, 1936290409&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;AVAudioSessionErrorCodeCannotStartPlaying&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;pla&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// 0x21706C61, 561015905&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;AVAudioSessionErrorCodeCannotStartRecording&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;rec&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// 0x21726563 …&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</summary><content type="html">&lt;p&gt;While looking at &lt;code&gt;AVAudioSessionErrorCode&lt;/code&gt; in Xcode, I noticed something
interesting in its definition:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;typedef&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CF_ENUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AVAudioInteger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AVAudioSessionErrorCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;AVAudioSessionErrorCodeNone&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;AVAudioSessionErrorCodeMediaServicesFailed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;msrv&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c1"&gt;// 0x6D737276, 1836282486&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;AVAudioSessionErrorCodeIsBusy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;act&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="c1"&gt;// 0x21616374, 560030580&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;AVAudioSessionErrorCodeIncompatibleCategory&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// 0x21636174, 560161140&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;AVAudioSessionErrorCodeCannotInterruptOthers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 0x21696E74, 560557684&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;AVAudioSessionErrorCodeMissingEntitlement&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;ent&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// 0x656E743F, 1701737535&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;AVAudioSessionErrorCodeSiriIsRecording&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;siri&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="c1"&gt;// 0x73697269, 1936290409&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;AVAudioSessionErrorCodeCannotStartPlaying&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;pla&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// 0x21706C61, 561015905&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;AVAudioSessionErrorCodeCannotStartRecording&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;rec&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// 0x21726563, 561145187&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;AVAudioSessionErrorCodeBadParam&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;-50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;AVAudioSessionErrorCodeInsufficientPriority&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;pri&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// 0x21707269, 561017449&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;AVAudioSessionErrorCodeResourceNotAvailable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// 0x21726573, 561145203&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;AVAudioSessionErrorCodeUnspecified&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;what&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="c1"&gt;// 0x77686174, 2003329396&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;AVAudioSessionErrorCodeExpiredSession&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;ses&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// 0x21736573, 561210739&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;AVAudioSessionErrorCodeSessionNotActive&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;inac&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;// 0x696e6163, 1768841571&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The technique Apple used in defining the values of this enum is called &lt;a href="https://en.wikipedia.org/wiki/FourCC"&gt;FourCC&lt;/a&gt;,
short for "four-character code". They are multi-character constants in C, but
they are not &lt;strong&gt;strings&lt;/strong&gt;; they're &lt;strong&gt;integers&lt;/strong&gt; created from the ASCII bytes of
each character. The comment after each value is the combined hex value of the
integer followed by that same value in base 10.&lt;/p&gt;
&lt;p&gt;For &lt;code&gt;AVAudioSessionErrorCodeMediaServicesFailed&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;m = 0x6D
s = 0x73
r = 0x72
v = 0x76

0x6D737276 = 1836282486
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The limitation is 4 ASCII characters, but with some creativity you can get
pretty far: &lt;code&gt;siri&lt;/code&gt; is obvious, &lt;code&gt;msrv&lt;/code&gt; is short for "media services", and &lt;code&gt;!rec&lt;/code&gt;
means "not recording". I particularly like &lt;code&gt;what&lt;/code&gt; for an unspecified error.&lt;/p&gt;
&lt;p&gt;These strings are especially useful while printing/logging, since you can get
the four-character code printed using &lt;code&gt;printf("%.4s", (char*)&amp;amp;error);&lt;/code&gt;. It's no
longer relevant to us in a Swift world where all of this is much easier, but
still a neat piece of history I learned more about.&lt;/p&gt;</content><category term="til"/></entry><entry><title>`Optional` has a `take()` function</title><link href="https://scottberrevoets.com/til/optional-has-a-take-function/" rel="alternate"/><published>2026-04-05T00:00:00-07:00</published><updated>2026-04-05T00:00:00-07:00</updated><author><name>Scott Berrevoets</name></author><id>tag:scottberrevoets.com,2026-04-05:/til/optional-has-a-take-function/</id><summary type="html">&lt;p&gt;After the introduction of &lt;code&gt;Optional.toggle()&lt;/code&gt; back in 2018, I thought I had
learned everything there is to &lt;code&gt;Optional&lt;/code&gt;, but today I learned about another
method: &lt;code&gt;Optional.take()&lt;/code&gt;. As the [documentation] points out, it returns the
value of a mutable optional (if not &lt;code&gt;nil&lt;/code&gt;) and then resets the variable to …&lt;/p&gt;</summary><content type="html">&lt;p&gt;After the introduction of &lt;code&gt;Optional.toggle()&lt;/code&gt; back in 2018, I thought I had
learned everything there is to &lt;code&gt;Optional&lt;/code&gt;, but today I learned about another
method: &lt;code&gt;Optional.take()&lt;/code&gt;. As the [documentation] points out, it returns the
value of a mutable optional (if not &lt;code&gt;nil&lt;/code&gt;) and then resets the variable to
&lt;code&gt;nil&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;myInt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;myOtherInt&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;myInt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;take&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// 42&lt;/span&gt;
&lt;span class="bp"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;myInt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// nil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Because this also works if &lt;code&gt;myInt&lt;/code&gt; was nil, &lt;code&gt;take()&lt;/code&gt; returns an optional as
well. Nothing groundbreaking, but could save you an occasional line of code.&lt;/p&gt;</content><category term="til"/></entry><entry><title>Claude can load files "lazily" or "greedily"</title><link href="https://scottberrevoets.com/til/claude-can-load-files-lazily-or-greedily/" rel="alternate"/><published>2026-03-20T00:00:00-07:00</published><updated>2026-03-20T00:00:00-07:00</updated><author><name>Scott Berrevoets</name></author><id>tag:scottberrevoets.com,2026-03-20:/til/claude-can-load-files-lazily-or-greedily/</id><summary type="html">&lt;p&gt;Typically, markdown files can referenced other files by &lt;em&gt;linking&lt;/em&gt; to them: &lt;code&gt;[My
File](myfile.md)&lt;/code&gt;. Claude understands this syntax, making it a nicely
portable way to reference files. When prompting in Claude Code, referencing
other files works through the @-syntax: &lt;code&gt;@path/to/file&lt;/code&gt;. This loads the file
into Claude's context …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Typically, markdown files can referenced other files by &lt;em&gt;linking&lt;/em&gt; to them: &lt;code&gt;[My
File](myfile.md)&lt;/code&gt;. Claude understands this syntax, making it a nicely
portable way to reference files. When prompting in Claude Code, referencing
other files works through the @-syntax: &lt;code&gt;@path/to/file&lt;/code&gt;. This loads the file
into Claude's context similar to the markdown linking.&lt;/p&gt;
&lt;p&gt;When Claude sees the @-syntax in a markdown file, it reads it as well. But
Claude doesn't treat the markdown and @ syntaxes as equals: &lt;em&gt;linking&lt;/em&gt; to a file
&lt;em&gt;may&lt;/em&gt; cause it to read that file when it feels like it's the right time for it.
The @-syntax will immediately and unconditionally inline that file into the
prompt.&lt;/p&gt;
&lt;p&gt;In short, the markdown linking syntax is akin to "lazy loading", whereas the
@-syntax is "greedy loading".&lt;/p&gt;</content><category term="til"/></entry><entry><title>macOS can remember SSH passphrases</title><link href="https://scottberrevoets.com/til/macos-can-remember-ssh-passphrases/" rel="alternate"/><published>2026-03-02T00:00:00-08:00</published><updated>2026-03-02T00:00:00-08:00</updated><author><name>Scott Berrevoets</name></author><id>tag:scottberrevoets.com,2026-03-02:/til/macos-can-remember-ssh-passphrases/</id><summary type="html">&lt;p&gt;Having to type out my password every time in SSH into a machine got a little
tedious for me. Turns out macOS can save these passphrases to the Keychain to
save some typing each time you try to connect to a host over SSH.&lt;/p&gt;
&lt;p&gt;To enable this, make sure you're …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Having to type out my password every time in SSH into a machine got a little
tedious for me. Turns out macOS can save these passphrases to the Keychain to
save some typing each time you try to connect to a host over SSH.&lt;/p&gt;
&lt;p&gt;To enable this, make sure you're &lt;strong&gt;using the macOS-native&lt;/strong&gt; &lt;code&gt;ssh&lt;/code&gt; version. I had
a homebrew version installed, which doesn't support this. Verify with &lt;code&gt;which
ssh&lt;/code&gt;, which should output &lt;code&gt;/usr/bin/ssh&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Then run the following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Add private key to Keychain&lt;/span&gt;
ssh-add&lt;span class="w"&gt; &lt;/span&gt;--apple-use-keychain&lt;span class="w"&gt; &lt;/span&gt;~/.ssh/id_ed25519

&lt;span class="c1"&gt;# SSH config to read from Keychain&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Host *&lt;/span&gt;
&lt;span class="s2"&gt;    UseKeychain yes&lt;/span&gt;
&lt;span class="s2"&gt;    AddKeysToAgent yes&lt;/span&gt;
&lt;span class="s2"&gt;    IdentityFile ~/.ssh/id_ed25519&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;~/.ssh/config
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is enough to make it work one-time, but to preserve this option across
reboots add the following line to your &lt;code&gt;~/.zshrc&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;apple&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;load&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;keychain&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now SSH works without having to enter a passphrase or password again. In theory
this also works with RSA keys, but not all modern servers support that for
security purposes. Try an &lt;code&gt;ed25519&lt;/code&gt; key instead if the steps above don't work
with an RSA key pair.&lt;/p&gt;</content><category term="til"/></entry><entry><title>git can tell you ignored files, and why</title><link href="https://scottberrevoets.com/til/git-can-tell-you-ignored-files-and-why/" rel="alternate"/><published>2026-02-10T00:00:00-08:00</published><updated>2026-02-10T00:00:00-08:00</updated><author><name>Scott Berrevoets</name></author><id>tag:scottberrevoets.com,2026-02-10:/til/git-can-tell-you-ignored-files-and-why/</id><summary type="html">&lt;p&gt;Today I created a new file at &lt;code&gt;./Resources/Environment.xcconfig&lt;/code&gt;, but git didn't
pick up on the file and didn't show it in &lt;code&gt;git status&lt;/code&gt;. I suspected the file was
gitignored, but nothing in my &lt;code&gt;.gitignore&lt;/code&gt; was obviously excluding this file.
&lt;code&gt;git status --ignored&lt;/code&gt; adds a section in to the …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Today I created a new file at &lt;code&gt;./Resources/Environment.xcconfig&lt;/code&gt;, but git didn't
pick up on the file and didn't show it in &lt;code&gt;git status&lt;/code&gt;. I suspected the file was
gitignored, but nothing in my &lt;code&gt;.gitignore&lt;/code&gt; was obviously excluding this file.
&lt;code&gt;git status --ignored&lt;/code&gt; adds a section in to the normal status output that lists
all the ignored files. My file was in it, but I still didn't know why.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git check-ignore&lt;/code&gt; as a bare command confirms the ignored status of the passed
in file, but adding the &lt;code&gt;-v&lt;/code&gt; flag indicates the &lt;em&gt;source&lt;/em&gt; of the ignore. I ran
&lt;code&gt;git check-ignore -v Resources/Environment.xcconfig&lt;/code&gt; and the exact line number
in gitignore showed what caused the file to be ignored.&lt;/p&gt;
&lt;p&gt;In my case, a different Environment.xcconfig existed before and had been
removed from the repo, but not from gitignore so it caused the new file to be
ignored as well.&lt;/p&gt;</content><category term="til"/></entry><entry><title>There is a term for words like i18n and a11y</title><link href="https://scottberrevoets.com/til/there-is-a-term-for-words-like-i18n-and-a11y/" rel="alternate"/><published>2026-01-27T00:00:00-08:00</published><updated>2026-01-27T00:00:00-08:00</updated><author><name>Scott Berrevoets</name></author><id>tag:scottberrevoets.com,2026-01-27:/til/there-is-a-term-for-words-like-i18n-and-a11y/</id><summary type="html">&lt;p&gt;They're called &lt;strong&gt;numeronyms&lt;/strong&gt; and they're common terms in tech to refer to long
words like "internationalization" (i18n) and accessibility (a11y). However,
they're also used outside of tech: K9, 24/7, and gr8 are all numeronyms as well.&lt;/p&gt;
&lt;p&gt;The official definition, according to &lt;a href="https://en.wikipedia.org/wiki/Numeronym"&gt;Wikipedia&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A numeronym is a word, usually an …&lt;/p&gt;&lt;/blockquote&gt;</summary><content type="html">&lt;p&gt;They're called &lt;strong&gt;numeronyms&lt;/strong&gt; and they're common terms in tech to refer to long
words like "internationalization" (i18n) and accessibility (a11y). However,
they're also used outside of tech: K9, 24/7, and gr8 are all numeronyms as well.&lt;/p&gt;
&lt;p&gt;The official definition, according to &lt;a href="https://en.wikipedia.org/wiki/Numeronym"&gt;Wikipedia&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A numeronym is a word, usually an abbreviation, composed partially or wholly
of numerals.&lt;/p&gt;
&lt;/blockquote&gt;</content><category term="til"/></entry></feed>