<?xml version="1.0" encoding="UTF-8" standalone="no"?><?xml-stylesheet href="/feed.xsl" type="text/xsl"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-US">
  <id>tag:deallocatedobjects.com,2005:/feed</id>
  <link href="https://deallocatedobjects.com" rel="alternate" type="text/html"/>
  <link href="https://deallocatedobjects.com/feed" rel="self" type="application/atom+xml"/>
  <link href="https://pubsubhubbub.appspot.com/" rel="hub"/>
  <title>Deallocated Objects</title>
  <subtitle>iOS, Mac OS X and Web application development, interface and interaction design with code snippets  and tips and tricks on building awesome stuff by Jake Marsh.</subtitle>
  <icon>https://deallocatedobjects.com/icon-512.png</icon>
  <updated>2026-10-24T13:00:00Z</updated>
  <entry>
    <id>tag:deallocatedobjects.com,2005:Post/201</id>
    <published>2026-10-24T13:00:00Z</published>
    <updated>2026-10-24T13:00:00Z</updated>
    <link href="https://deallocatedobjects.com/posts/making-my-blog-readable-by-ai-agents" rel="alternate" type="text/html"/>
    <link href="https://deallocatedobjects.com/posts/making-my-blog-readable-by-ai-agents.md" rel="alternate" type="text/markdown"/>
    <category label="This Blog" term="this-blog"/>
    <category label="AI &amp; Agents" term="ai-agents"/>
    <summary>A lot of the people reading this blog now aren’t people. They’re agents: Claude, ChatGPT, Perplexity and friends, fetching a page because somebody asked them a question. This week I spent an evening making Deallocated Objects nice to read for them (and for search engines while I was in there). The…</summary>
    <title>Making My Blog Readable By AI Agents</title>
    <content type="html">&lt;p&gt;A lot of the people reading this blog now aren’t people. They’re agents: Claude, ChatGPT, Perplexity and friends, fetching a page because somebody asked them a question. This week I spent an evening making Deallocated Objects nice to read for them (and for search engines while I was in there).&lt;/p&gt;
&lt;p&gt;The blog is a static site served by nginx, so everything here is either a file generated at build time or a few lines of nginx config. No framework required.&lt;/p&gt;
&lt;h2 id="markdown-for-every-post" data-level="3"&gt;Markdown For Every Post&lt;/h2&gt;
&lt;p&gt;HTML is a lot of noise for an agent to wade through. Every post now also exists as plain markdown. Add &lt;code&gt;.md&lt;/code&gt; to any post URL:&lt;/p&gt;
&lt;figure class="code" data-lang="text"&gt;&lt;pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"&gt;&lt;code&gt;&lt;span class="line"&gt;&lt;span&gt;https://deallocatedobjects.com/posts/the-art-of-the-reveal.md&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;Each file starts with a little YAML frontmatter (title, canonical URL, author, dates, a short description), then the post. The older posts were written in markdown to begin with, so the build just cleans those up and points their images at the self-hosted copies.&lt;/p&gt;
&lt;p&gt;The nicer trick is content negotiation. If a client asks for markdown in its &lt;code&gt;Accept&lt;/code&gt; header, it gets markdown from the &lt;em&gt;normal&lt;/em&gt; URL, no &lt;code&gt;.md&lt;/code&gt; needed. In nginx that’s a &lt;code&gt;map&lt;/code&gt; plus one line per location:&lt;/p&gt;
&lt;figure class="code" data-lang="nginx"&gt;&lt;figcaption&gt;&lt;span class="lang"&gt;nginx&lt;/span&gt;&lt;/figcaption&gt;&lt;pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"&gt;&lt;code&gt;&lt;span class="line"&gt;&lt;span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"&gt;# http context&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt;map&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt; $&lt;/span&gt;&lt;span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"&gt;http_accept&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt; $deallocated_md {&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"&gt;    default&lt;/span&gt;&lt;span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"&gt; ""&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"&gt;    "~*text/markdown"&lt;/span&gt;&lt;span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"&gt; ".md"&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"&gt;# server block&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt;location&lt;/span&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt; ~&lt;/span&gt;&lt;span style="--shiki-light:#032F62;--shiki-dark:#DBEDFF"&gt; ^/posts/[^/.]+$ &lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt;    if&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt; ($deallocated_md) { &lt;/span&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt;rewrite&lt;/span&gt;&lt;span style="--shiki-light:#032F62;--shiki-dark:#DBEDFF"&gt; ^ $&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;uri.md &lt;/span&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt;last&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt;    try_files &lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;$uri.html &lt;/span&gt;&lt;span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"&gt;=404&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;Try it:&lt;/p&gt;
&lt;figure class="code" data-lang="bash"&gt;&lt;figcaption&gt;&lt;span class="lang"&gt;Shell&lt;/span&gt;&lt;/figcaption&gt;&lt;pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"&gt;&lt;code&gt;&lt;span class="line"&gt;&lt;span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"&gt;curl&lt;/span&gt;&lt;span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"&gt; -H&lt;/span&gt;&lt;span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"&gt; "Accept: text/markdown"&lt;/span&gt;&lt;span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"&gt; https://deallocatedobjects.com/posts/the-art-of-the-reveal&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;Two details matter here. Every response sends &lt;code&gt;Vary: Accept&lt;/code&gt;, so a cache in the middle never hands HTML to an agent or markdown to a browser. And the &lt;code&gt;.md&lt;/code&gt; responses send a &lt;code&gt;Link: &amp;lt;...&amp;gt;; rel=&amp;quot;canonical&amp;quot;&lt;/code&gt; header pointing at the HTML page, plus &lt;code&gt;X-Robots-Tag: noindex&lt;/code&gt;, so Google doesn’t treat them as duplicate copies of every post.&lt;/p&gt;
&lt;h2 id="llms-txt" data-level="3"&gt;llms.txt&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://deallocatedobjects.com/llms.txt"&gt;&lt;code&gt;/llms.txt&lt;/code&gt;&lt;/a&gt; is a plain markdown file at the root of the site that tells an agent what it’s looking at. Mine has a one-line summary of the blog, how to get markdown, where the feeds are, how I’d like posts cited, and a list of every post with a link to its markdown and a one-line summary.&lt;/p&gt;
&lt;p&gt;There’s also &lt;a href="https://deallocatedobjects.com/llms-full.txt"&gt;&lt;code&gt;/llms-full.txt&lt;/code&gt;&lt;/a&gt;, which is the full text of every post in one file (about 225 KB), and &lt;code&gt;/index.md&lt;/code&gt;, which is the homepage as markdown. Every response also carries a &lt;code&gt;Link&lt;/code&gt; header pointing at &lt;code&gt;llms.txt&lt;/code&gt;, so an agent can find it without guessing.&lt;/p&gt;
&lt;h2 id="robots-txt" data-level="3"&gt;robots.txt&lt;/h2&gt;
&lt;p&gt;Silence in &lt;code&gt;robots.txt&lt;/code&gt; technically means “allowed”, but I’d rather be explicit. Mine allows everyone, then names the big ones explicitly (&lt;code&gt;GPTBot&lt;/code&gt;, &lt;code&gt;ClaudeBot&lt;/code&gt;, &lt;code&gt;PerplexityBot&lt;/code&gt;, &lt;code&gt;Google-Extended&lt;/code&gt; and about a dozen more) and adds a &lt;code&gt;Content-Signal&lt;/code&gt; line:&lt;/p&gt;
&lt;figure class="code" data-lang="text"&gt;&lt;pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"&gt;&lt;code&gt;&lt;span class="line"&gt;&lt;span&gt;User-agent: *&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;Allow: /&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span&gt;Content-Signal: search=yes, ai-input=yes, ai-train=yes&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;That’s the right call for a blog. I &lt;em&gt;want&lt;/em&gt; to be cited. A product site might choose differently.&lt;/p&gt;
&lt;h2 id="feeds" data-level="3"&gt;Feeds&lt;/h2&gt;
&lt;p&gt;The old Atom feed at &lt;code&gt;/feed&lt;/code&gt; still works, and every entry now has a summary and a &lt;code&gt;text/markdown&lt;/code&gt; alternate link. I also added a &lt;a href="https://deallocatedobjects.com/feed.json"&gt;JSON Feed&lt;/a&gt;, where each item carries a &lt;code&gt;_markdown_url&lt;/code&gt; (JSON Feed allows custom keys that start with an underscore).&lt;/p&gt;
&lt;h2 id="meta-tags" data-level="3"&gt;Meta Tags&lt;/h2&gt;
&lt;p&gt;The old site had almost none of these, so every page got the full set:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a real meta description, a canonical URL, a robots tag and &lt;code&gt;lang=&amp;quot;en&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Open Graph and Twitter card tags, including &lt;code&gt;article:published_time&lt;/code&gt; and &lt;code&gt;modified_time&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;JSON-LD structured data: &lt;code&gt;BlogPosting&lt;/code&gt; and a breadcrumb on every post, with &lt;code&gt;WebSite&lt;/code&gt;, &lt;code&gt;Blog&lt;/code&gt; and a &lt;code&gt;Person&lt;/code&gt; for me on the index pages&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The descriptions were sneakier than I expected. The old database stored a pre-made description for each post, but it had run the paragraphs together (“…your app.The RevealWhat do I mean…”) and a few contained raw &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; HTML. Now the build makes its summaries from the rendered post text instead.&lt;/p&gt;
&lt;p&gt;Two more fixes from the old 2014 markup. The viewport tag had &lt;code&gt;maximum-scale=1&lt;/code&gt;, which stops people from pinch-zooming on their phones, so that’s gone. And there was no &lt;code&gt;og:image&lt;/code&gt; at all, so when I shared a post, X and Threads grabbed the first image on the page, which happens to be the Little Bites of Cocoa banner at the top. Every post has a proper 1200x630 preview card now.&lt;/p&gt;
&lt;h2 id="indexnow" data-level="3"&gt;IndexNow&lt;/h2&gt;
&lt;p&gt;Last, an &lt;a href="https://www.indexnow.org"&gt;IndexNow&lt;/a&gt; key and a script that pings Bing and friends whenever a post is published. Google doesn’t participate, but Bing’s index feeds a lot of AI search tools, so it’s worth a 30-line script.&lt;/p&gt;
&lt;p&gt;None of this took long, and most of it is just files. If you have a blog, &lt;code&gt;llms.txt&lt;/code&gt; and &lt;code&gt;.md&lt;/code&gt; versions of your posts are a nice place to start. Here’s this post:&lt;/p&gt;
&lt;figure class="code" data-lang="bash"&gt;&lt;figcaption&gt;&lt;span class="lang"&gt;Shell&lt;/span&gt;&lt;/figcaption&gt;&lt;pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"&gt;&lt;code&gt;&lt;span class="line"&gt;&lt;span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"&gt;curl&lt;/span&gt;&lt;span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"&gt; -H&lt;/span&gt;&lt;span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"&gt; "Accept: text/markdown"&lt;/span&gt;&lt;span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"&gt; https://deallocatedobjects.com/posts/making-my-blog-readable-by-ai-agents&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;The rest of how this site is built is in the &lt;a href="https://deallocatedobjects.com/colophon"&gt;colophon&lt;/a&gt;.&lt;/p&gt;</content>
    <author>
      <name>Jake Marsh</name>
    </author>
  </entry>
  <entry>
    <id>tag:deallocatedobjects.com,2005:Post/200</id>
    <published>2026-10-23T13:00:00Z</published>
    <updated>2026-10-23T13:00:00Z</updated>
    <link href="https://deallocatedobjects.com/posts/moving-out-of-ubuntu-10-04" rel="alternate" type="text/html"/>
    <link href="https://deallocatedobjects.com/posts/moving-out-of-ubuntu-10-04.md" rel="alternate" type="text/markdown"/>
    <category label="This Blog" term="this-blog"/>
    <summary>This blog has been sitting on the same Linode server for a very long time. Deallocated Objects started on WordPress in 2010, moved to a little Rails app I wrote around 2014, and that Rails app has been quietly running ever since on a box I’ve been paying for since 2012. This month I finally moved…</summary>
    <title>Moving Out Of Ubuntu 10.04</title>
    <content type="html">&lt;p&gt;This blog has been sitting on the same Linode server for a very long time. Deallocated Objects started on WordPress in 2010, moved to a little Rails app I wrote around 2014, and that Rails app has been quietly running ever since on a box I’ve been paying for since 2012.&lt;/p&gt;
&lt;p&gt;This month I finally moved it. It turned into a fun little archaeology project, so here’s how it went.&lt;/p&gt;
&lt;h2 id="what-was-on-the-box" data-level="3"&gt;What Was On The Box&lt;/h2&gt;
&lt;p&gt;Opening it up was a trip:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Ubuntu 10.04 LTS&lt;/strong&gt;, 32-bit. Ubuntu stopped supporting 10.04 in 2015.&lt;/li&gt;
&lt;li&gt;nginx 1.2.7, Postgres 8.4, MySQL 5.1, Redis, and the Rails 4 app running on unicorn with sidekiq next to it.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sshd&lt;/code&gt; only offered &lt;code&gt;ssh-rsa&lt;/code&gt; and &lt;code&gt;ssh-dss&lt;/code&gt; host keys, which modern OpenSSH won’t use without some coaxing.&lt;/li&gt;
&lt;li&gt;The HTTPS certificate expired on &lt;strong&gt;November 28th, 2016&lt;/strong&gt;. It only spoke TLSv1 anyway. So HTTPS on this blog has been broken for about ten years, and I never noticed.&lt;/li&gt;
&lt;li&gt;About 6 GB of the 9 GB on disk was Rails logs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It also had the backend for the original Conditions iOS app from 2013, a Huginn install from 2014, and an even older blog app with exactly one article in it. All for $29 a month.&lt;/p&gt;
&lt;h2 id="getting-the-data-out" data-level="3"&gt;Getting The Data Out&lt;/h2&gt;
&lt;p&gt;I didn’t have a working password or SSH key for the box anymore. Linode has a rescue mode that boots a small Linux distro called Finnix, so I booted into that, mounted the old disk read-only, and ran Postgres 8.4 out of a scratch overlay so nothing would ever write to the original disk. From there, &lt;code&gt;pg_dump&lt;/code&gt; for every database, a test restore to make sure the dumps were good, and a full copy of the filesystem.&lt;/p&gt;
&lt;p&gt;The blog’s database turned out to be tiny: 82 rows in the &lt;code&gt;posts&lt;/code&gt; table (80 published, 2 drafts), plus 9 Safari push notification subscribers. Hi, nine people!&lt;/p&gt;
&lt;h2 id="going-static" data-level="3"&gt;Going Static&lt;/h2&gt;
&lt;p&gt;A blog that hasn’t changed since 2015 doesn’t need a Rails app, a database, and a background job queue to serve it. So I rebuilt it as a static site: a Bun and TypeScript script, about 300 lines with no runtime dependencies, that reads the posts and writes out plain HTML files. nginx serves those files and there’s no app process at all.&lt;/p&gt;
&lt;p&gt;The first surprise: the &lt;code&gt;html_content&lt;/code&gt; column was empty for every post. The old app rendered markdown on every single request with Redcarpet and Pygments. Instead of trying to recreate Redcarpet’s exact quirks, I crawled the live site before shutting anything down and kept each post’s rendered HTML exactly as the old app produced it. Same output as before.&lt;/p&gt;
&lt;p&gt;The second surprise: the order of posts on the index pages. The app sorted by &lt;code&gt;published_at&lt;/code&gt;, and when two posts shared a date, Postgres broke the tie however it felt like that day. Re-sorting them would have shuffled a few posts around, so the index order and every post’s previous/next links are frozen from the crawl.&lt;/p&gt;
&lt;h2 id="every-old-url-still-works" data-level="3"&gt;Every Old URL Still Works&lt;/h2&gt;
&lt;p&gt;This was the part I cared most about. People have linked to these posts for years, and I didn’t want to break any of them:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/posts/&amp;lt;slug&amp;gt;&lt;/code&gt; and &lt;code&gt;/posts/&amp;lt;id&amp;gt;&lt;/code&gt; (the Rails app answered both; the id now 301s to the slug)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/posts/&amp;lt;slug&amp;gt;.json&lt;/code&gt; and &lt;code&gt;/posts.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/feed&lt;/code&gt;, &lt;code&gt;/feed.atom&lt;/code&gt; and &lt;code&gt;/posts.atom&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;the sitemaps, &lt;code&gt;robots.txt&lt;/code&gt; and &lt;code&gt;humans.txt&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;five URLs from the 2010 WordPress days, which still 301 to the right posts&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pagination was the one tricky bit for a static site, since the old URLs use a query string (&lt;code&gt;/posts?page=3&lt;/code&gt;). nginx handles that by mapping the query to a pre-built file:&lt;/p&gt;
&lt;figure class="code" data-lang="nginx"&gt;&lt;figcaption&gt;&lt;span class="lang"&gt;nginx&lt;/span&gt;&lt;/figcaption&gt;&lt;pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"&gt;&lt;code&gt;&lt;span class="line"&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt;location&lt;/span&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt; =&lt;/span&gt;&lt;span style="--shiki-light:#032F62;--shiki-dark:#DBEDFF"&gt; /posts &lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt;    if&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt; ($arg_page &lt;/span&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt;~ &lt;/span&gt;&lt;span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"&gt;"^[0-9]+$"&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;) { &lt;/span&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt;rewrite&lt;/span&gt;&lt;span style="--shiki-light:#032F62;--shiki-dark:#DBEDFF"&gt; ^ /_pages/$&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;arg_page.html &lt;/span&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt;last&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt;    try_files &lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;/index.html &lt;/span&gt;&lt;span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"&gt;=404&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;To check all of it, I wrote a little verify script that requests every URL on the new site (358 of them) and compares the title and text of every page against the crawl of the old one. 358 URLs, 0 failures, 101 out of 101 pages matching.&lt;/p&gt;
&lt;p&gt;Along the way I found a bug that had been live for years. The post &lt;a href="https://deallocatedobjects.com/posts/7-keys-to-unlock-an-apple-design-award"&gt;“7 Keys to Unlock an Apple Design Award”&lt;/a&gt; was a 404 on the old site. The app decided whether a URL was an id or a slug by calling &lt;code&gt;to_i&lt;/code&gt; on it, and in Ruby &lt;code&gt;&amp;quot;7-keys-to-unlock-an-apple-design-award&amp;quot;.to_i&lt;/code&gt; is &lt;code&gt;7&lt;/code&gt;. So it went looking for post #7, which doesn’t exist. It works now.&lt;/p&gt;
&lt;h2 id="images" data-level="3"&gt;Images&lt;/h2&gt;
&lt;p&gt;Posts referenced 94 image URLs. The ones in my own S3 buckets were all still there, so I downloaded them. A bunch of others were already dead, mostly old CloudApp (&lt;code&gt;f.cl.ly&lt;/code&gt;) links. I pulled 22 of those back out of the Wayback Machine. In the end 70 images are self-hosted with the site now, and 19 CloudApp images are just gone. RIP.&lt;/p&gt;
&lt;h2 id="what-didnt-make-it" data-level="3"&gt;What Didn’t Make It&lt;/h2&gt;
&lt;p&gt;Google Analytics (the old Universal Analytics kind, which Google shut down in 2023), Chartbeat, the Facebook SDK, the share buttons (the Hacker News button’s host doesn’t even exist anymore), jQuery, and the Safari push notification endpoints. Sorry, nine subscribers.&lt;/p&gt;
&lt;p&gt;The FeedBurner feed link still works. Wild.&lt;/p&gt;
&lt;p&gt;Now the whole site is a folder of files. Deploying is a build and an &lt;code&gt;rsync&lt;/code&gt;, HTTPS works again for the first time since 2016, and the old Linode is deleted. I did most of this with Claude Code, which was great at the careful, boring parts, like diffing every page of the crawl against the rebuild.&lt;/p&gt;
&lt;p&gt;It feels really good to have this place back. I’ve already started writing here again.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Update: the site has picked up a few more features since this post. How it all works today is in the &lt;a href="https://deallocatedobjects.com/colophon"&gt;colophon&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</content>
    <author>
      <name>Jake Marsh</name>
    </author>
  </entry>
  <entry>
    <id>tag:deallocatedobjects.com,2005:Post/199</id>
    <published>2026-10-22T19:02:27Z</published>
    <updated>2026-10-22T19:02:27Z</updated>
    <link href="https://deallocatedobjects.com/posts/everything-that-broke-on-october-21st" rel="alternate" type="text/html"/>
    <link href="https://deallocatedobjects.com/posts/everything-that-broke-on-october-21st.md" rel="alternate" type="text/markdown"/>
    <category label="Security" term="security"/>
    <summary>Yesterday (Wednesday, October 21st) was one of the strangest days in security I can remember. By lunchtime TechCrunch was running a story titled “The internet is having a very bad cybersecurity day”. None of these incidents are connected to each other. They just kept landing, one after another…</summary>
    <title>Everything That Broke On October 21st</title>
    <content type="html">&lt;p&gt;Yesterday (Wednesday, October 21st) was one of the strangest days in security I can remember. By lunchtime TechCrunch was running a story titled &lt;a href="https://techcrunch.com/2026/10/21/the-internet-is-having-a-very-bad-cybersecurity-day/"&gt;“The internet is having a very bad cybersecurity day”&lt;/a&gt;. None of these incidents are connected to each other. They just kept landing, one after another, all day long.&lt;/p&gt;
&lt;p&gt;Here’s a rundown of what happened, roughly ordered by how much it should worry you, with as many technical details as I could dig up.&lt;/p&gt;
&lt;h2 id="php2shell" data-level="3"&gt;PHP2Shell&lt;/h2&gt;
&lt;p&gt;The big one. PHP shipped emergency releases (&lt;strong&gt;8.5.12&lt;/strong&gt;, &lt;strong&gt;8.4.27&lt;/strong&gt;, &lt;strong&gt;8.3.36&lt;/strong&gt; and &lt;strong&gt;8.2.35&lt;/strong&gt;) at 14:00 UTC to fix two bugs that allow unauthenticated remote code execution. Both are rated CVSS v4 9.2.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CVE-2026-109618&lt;/strong&gt; is out-of-memory handling interrupting PHP while internal request data is only partly initialized. Request cleanup then treats that half-built data as valid. &lt;strong&gt;CVE-2026-109619&lt;/strong&gt; is request-owned state that survives after its memory is freed, so the next request on the same worker uses it before initialization resets it. A classic use-after-free.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://www.php.net/archive/2026.php"&gt;php.net advisory&lt;/a&gt; says every PHP install serving web requests is affected: PHP-FPM, &lt;code&gt;mod_php&lt;/code&gt;, &lt;code&gt;php-cgi&lt;/code&gt;, FrankenPHP and LiteSpeed. They consider &lt;code&gt;mod_php&lt;/code&gt; the riskiest.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.upwind.io/feed/initial-assessment-php2shell-two-critical-unpatched-php-rces-cve-2026-109618-cve-2026-109619"&gt;Upwind traced&lt;/a&gt; one confirmed path to &lt;code&gt;php_session_rfc1867_callback&lt;/code&gt; in &lt;code&gt;ext/session/session.c&lt;/code&gt;, the code that tracks upload progress on multipart POSTs. PHP caches a couple of &lt;code&gt;zval *&lt;/code&gt; pointers while it counts bytes, then a session re-read destroys the hash table those pointers live in. The only precondition is &lt;code&gt;session.upload_progress.enabled=1&lt;/code&gt;, which is the &lt;em&gt;default&lt;/em&gt;. A stock &lt;code&gt;php.ini&lt;/code&gt; is enough. Exploiting it needs a build-specific offset and wrong guesses crash the worker, so a burst of 502s from behind nginx is a decent sign someone’s knocking.&lt;/p&gt;
&lt;p&gt;The release was originally planned for today (the 22nd). Details and screenshots of an exploit tool leaked on the 20th, so PHP pulled it forward a day. Amazon’s MadPot honeypots logged an exploitation attempt on &lt;strong&gt;October 17th&lt;/strong&gt;, four days before anything was public, and nobody knows yet how that person got the details.&lt;/p&gt;
&lt;p&gt;Calif found the bugs using OpenAI’s GPT-5.6 Sol, and Wiz found them independently.&lt;/p&gt;
&lt;p&gt;Then governments started pulling sites. The NSA said to take a system offline if you can’t patch it right away. The UK took &lt;a href="https://www.theregister.com/security/2026/10/22/php2shell-puts-dozens-of-uk-government-services-out-of-service/5304840"&gt;32 government services offline&lt;/a&gt; (and four more partially). The Netherlands, Finland, French local government sites, a Danish school platform and Australia’s own cyber agency website went dark too. By that evening Pantheon alone had blocked over 60,000 suspicious requests, and Cloudflare was watching attackers mutate payloads to slip past its WAF rules.&lt;/p&gt;
&lt;p&gt;If you can’t upgrade yet, PHP lists stopgaps that shrink the attack surface (they’re clear that only the update makes you safe). Put these in &lt;code&gt;php.ini&lt;/code&gt; or your FPM pool config, since &lt;code&gt;ini_set()&lt;/code&gt; and &lt;code&gt;.user.ini&lt;/code&gt; apply too late:&lt;/p&gt;
&lt;figure class="code" data-lang="ini"&gt;&lt;figcaption&gt;&lt;span class="lang"&gt;INI&lt;/span&gt;&lt;/figcaption&gt;&lt;pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"&gt;&lt;code&gt;&lt;span class="line"&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt;session.upload_progress.enabled&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt; = 0&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt;post_max_size&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt; = 8M         &lt;/span&gt;&lt;span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"&gt;; 32M if you need uploads&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt;fatal_error_backtraces&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt; = 0 &lt;/span&gt;&lt;span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"&gt;; PHP 8.5 only&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;p&gt;They also suggest &lt;code&gt;php_admin_value[memory_limit] = -1&lt;/code&gt; in the FPM pool. Restart every worker afterwards. Also watch out for fake “PoC” repos on GitHub. JFrog has already found some carrying malware.&lt;/p&gt;
&lt;h2 id="litellm-1-107-0" data-level="3"&gt;LiteLLM 1.107.0&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/BerriAI/litellm"&gt;LiteLLM&lt;/a&gt;, the Python proxy a lot of folks use to put a bunch of LLM providers behind one API, had a trojanized release up on PyPI, Docker Hub and GHCR for about two hours. It’s the project’s second compromise this year.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.stepsecurity.io/blog/litellm-1-107-0-compromised-pypi-backdoored-wheels-credential-stealer"&gt;StepSecurity’s reconstruction&lt;/a&gt; is wild. The attacker had stolen GitHub CLI OAuth tokens belonging to three BerriAI engineers. An org owner’s token overrode branch protection at 10:08:34 UTC (five seconds after protection rejected the change) and rewrote the private release workflow to build from a lookalike repo, &lt;code&gt;Berri-AI/litellm&lt;/code&gt;. Note the hyphen. A second engineer’s token approved the production deploy, and a third one deleted the workflow run records.&lt;/p&gt;
&lt;p&gt;Since the attacker changed the &lt;em&gt;trusted&lt;/em&gt; workflow itself, all the hardening LiteLLM added after its March incident (Trusted Publishing, immutable tags, signed images) still applied to the bad build. The malicious packages shipped with valid provenance.&lt;/p&gt;
&lt;p&gt;The wheels drop a 12-byte file called &lt;code&gt;cook.pth&lt;/code&gt; that contains &lt;code&gt;import cook&lt;/code&gt;. Python’s &lt;code&gt;site&lt;/code&gt; module executes import lines in &lt;code&gt;.pth&lt;/code&gt; files on every interpreter start, so the stealer runs whenever Python starts on that machine. You never have to &lt;code&gt;import litellm&lt;/code&gt; at all. Clever, and gross.&lt;/p&gt;
&lt;p&gt;It grabs SSH and GPG keys, cloud credentials, cloud metadata endpoints, CI secrets, every process’s &lt;code&gt;/proc/&amp;lt;pid&amp;gt;/environ&lt;/code&gt;, browser passwords, and the config for AI coding tools like Claude Code, Cursor and Codex. If &lt;code&gt;DATABASE_URL&lt;/code&gt; is set it also dumps LiteLLM’s own tables, which are full of upstream provider keys. Everything gets encrypted (AES-256, with the key wrapped in RSA-4096) and POSTed to &lt;code&gt;http://www.litellm.dev/healthcheck&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A second hook in &lt;code&gt;sitecustomize.py&lt;/code&gt; forks a daemon that checks a GitHub repo under the account &lt;code&gt;letters9813&lt;/code&gt; every hour for a hostname, then fetches whatever is at &lt;code&gt;/codepy&lt;/code&gt; on that host and &lt;code&gt;exec()&lt;/code&gt;s it. That GitHub account is gone now, but infected machines are still polling, and anyone who re-registers the name could run code on all of them.&lt;/p&gt;
&lt;p&gt;Wiz estimates about 49,000 downloads. If you picked up 1.107.0 between 11:39 and 13:23 UTC (or pulled a floating Docker tag before 14:20), kill the loader process &lt;em&gt;before&lt;/em&gt; you run &lt;code&gt;pip&lt;/code&gt; again, because every Python start re-runs the stealer. Then rebuild the machine, pin &lt;code&gt;litellm==1.106.2&lt;/code&gt;, and rotate every credential that box could reach.&lt;/p&gt;
&lt;p&gt;BerriAI suspects the GitHub tokens leaked out of an internet-exposed internal LiteLLM deployment that logged response bodies, including responses where Claude Code had included the tokens. That part isn’t confirmed. I use Claude Code all day, so this one hit close to home. Anything that shows up in an agent’s output can end up sitting in somebody’s logs.&lt;/p&gt;
&lt;h2 id="railway" data-level="3"&gt;Railway&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://blog.railway.com/p/security-disclosure-wednesday-october-21st"&gt;Railway disclosed&lt;/a&gt; that someone got into the systems that store customer environment variables, and for some customers walked away with the decrypted values. Railway says it’s part of a series of attacks on platform companies aimed at stealing keys for AI inference providers. They paused &lt;em&gt;all&lt;/em&gt; deployments, invalidated API tokens and OAuth sessions, and warned upstream AI providers so they could revoke keys early. Researcher Taylor Monahan traced crypto theft back to at least two hours before the disclosure went up.&lt;/p&gt;
&lt;p&gt;No root cause or customer count yet. If you’re on Railway, rotate your secrets at the provider itself, since an old key keeps working until the provider revokes it. Their follow-up post specifically says to check your OpenRouter usage.&lt;/p&gt;
&lt;h2 id="connectwise-and-storm-0940" data-level="3"&gt;ConnectWise And Storm-0940&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.microsoft.com/en-us/security/blog/2026/10/21/storm-0940-uses-artex-agentic-ai-connectwise-supply-chain-compromise/"&gt;Microsoft published&lt;/a&gt; its attribution for last week’s ConnectWise Cloud Backup compromise: &lt;strong&gt;Storm-0940&lt;/strong&gt;, a China-aligned group. The attacker took control of the identities of ConnectWise’s multi-tenant Microsoft 365 apps and then signed in &lt;em&gt;as those apps&lt;/em&gt; to customer tenants, riding the trust each tenant had already granted. Nobody had to type a password or approve an MFA prompt. Microsoft counted sign-in attempts against more than 2,250 tenants, peaking around 2,900 attempts per minute.&lt;/p&gt;
&lt;p&gt;They automated it with ARTEX, an open-source multi-agent LLM pentest framework (Go backend, Next.js frontend) that works with any model. In some victims it went from first contact to privileged cloud access in under three hours, and in one tenant a ConnectWise app assigned &lt;em&gt;itself&lt;/em&gt; Global Administrator.&lt;/p&gt;
&lt;p&gt;Microsoft’s advice is to disable the ConnectWise apps (search by app ID, display names can lie) and hold off on deleting them so you keep the evidence. Disabling them won’t remove credentials the attacker already added to your other apps, so go check those too. ConnectWise started a phased restore yesterday.&lt;/p&gt;
&lt;h2 id="gitlab-cve-2026-110264" data-level="3"&gt;GitLab CVE-2026-110264&lt;/h2&gt;
&lt;p&gt;A CVSS 10.0. An integer overflow in the regex compiler lets an unauthenticated user run code on the server, and it affects every version back to &lt;strong&gt;7.0&lt;/strong&gt;. That’s 2014! It’s &lt;a href="https://docs.gitlab.com/releases/patches/patch-release-gitlab-19-5-1-released/"&gt;fixed in&lt;/a&gt; 18.11.13, 19.0.10, 19.1.9, 19.2.9, 19.3.6, 19.4.3 and 19.5.1. There are no workarounds.&lt;/p&gt;
&lt;p&gt;GitLab hasn’t published the full path. &lt;a href="https://lilting.ch/en/articles/gitlab-cve-2026-110264-org-mode-rce"&gt;Secondary write-ups&lt;/a&gt; point at Onigmo (Ruby’s regex engine), reached by rendering an Org-mode &lt;code&gt;.org&lt;/code&gt; file, which lines up with GitLab’s own detection rules being named “Onigmo native worker crash” and “Org mode render error burst.” This is a &lt;em&gt;different&lt;/em&gt; bug from the CVSS 10 that GitLab fixed on October 12th. Patched last week? You need to patch again. No exploitation reported so far.&lt;/p&gt;
&lt;h2 id="emudhras-single-byte-ca-keys" data-level="3"&gt;eMudhra’s Single-Byte CA Keys&lt;/h2&gt;
&lt;p&gt;This one technically broke a few days earlier, but the fallout peaked yesterday. eMudhra’s stock fell about 20% after an exchange filing, and it was the #2 security story on Hacker News.&lt;/p&gt;
&lt;p&gt;Eight eMudhra intermediate CAs (six for TLS, two for code signing), cross-signed by Sectigo’s root, had P-384 private keys that were numbers between 0 and 255. &lt;a href="https://scotthelme.co.uk/eight-bits-of-trust-emudhras-ca-keys-cracked-in-milliseconds/"&gt;Scott Helme’s write-up&lt;/a&gt; lists all of them. One key is &lt;strong&gt;4&lt;/strong&gt;. Getting a private key back from its public certificate takes at most 255 tries.&lt;/p&gt;
&lt;p&gt;A bug in the April 2025 key ceremony script created a second, weak key pair on the HSM, and similar key names meant the wrong one got used for the CSR. These keys have effectively been public for about 17 months. Chrome found no TLS certs chaining to them in CT logs, and browsers blocked them within hours of the report. The real exposure is non-browser clients (&lt;code&gt;curl&lt;/code&gt;, Python, Go, Java, package managers) that check neither CT nor revocation. Update your CA bundles (&lt;code&gt;ca-certificates&lt;/code&gt;, &lt;code&gt;certifi&lt;/code&gt;, Node, Homebrew) and rebuild your container images.&lt;/p&gt;
&lt;h2 id="node-js" data-level="3"&gt;Node.js&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://nodejs.org/en/blog/vulnerability/october-2026-security-releases"&gt;Node.js 22.23.4, 24.22.1 and 26.12.1&lt;/a&gt; fix CVE-2026-111497, an out-of-bounds write in the Brotli decoder that allows remote code execution. OpenAI’s Codex Security was one of the reporters. Anything that decompresses Brotli from an attacker (request bodies, fetched responses) is exposed, and Vercel pushed emergency firewall rules.&lt;/p&gt;
&lt;p&gt;Also in that release: CVE-2026-106074, where &lt;code&gt;crypto.randomUUID()&lt;/code&gt; and &lt;code&gt;randomInt()&lt;/code&gt; had their entropy caches baked into user-land startup snapshots and single executable apps. Every process launched from one of those produced &lt;em&gt;the same&lt;/em&gt; “random” values. Yikes. The release drops eMudhra from Node’s CA bundle too.&lt;/p&gt;
&lt;h2 id="gmo-onamae-com-desktop-cloud" data-level="3"&gt;GMO Onamae.com Desktop Cloud&lt;/h2&gt;
&lt;p&gt;Over in Japan, GMO started shutting down &lt;em&gt;every&lt;/em&gt; Onamae.com Desktop Cloud server at 04:00 JST after finding malware planted on customer machines. Up to 26,739 servers could be affected. The service is mostly Windows desktops running automated forex trading, and customers have reported unauthorized trades on their accounts. GMO hasn’t published any malware details or IOCs yet.&lt;/p&gt;
&lt;h2 id="the-rest-of-the-day" data-level="3"&gt;The Rest Of The Day&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;CISA and the FBI put out a &lt;a href="https://www.bleepingcomputer.com/news/security/cisa-and-fbi-safepay-ransomware-has-hit-over-600-critical-infrastructure-organizations/"&gt;joint advisory (AA26-293A)&lt;/a&gt; on SafePay ransomware, which has hit 600+ critical infrastructure organizations since August 2024.&lt;/li&gt;
&lt;li&gt;Palo Alto’s GlobalProtect auth bypass (CVE-2026-0257) is &lt;a href="https://www.helpnetsecurity.com/2026/10/21/ransomware-groups-exploit-palo-alto-globalprotect-cve-2026-0257/"&gt;now a front door&lt;/a&gt; for the Qilin, Inc and Settra ransomware groups. Patching doesn’t kick out VPN sessions that already exist, so kill those after you patch.&lt;/li&gt;
&lt;li&gt;OVHcloud’s CLI had release binaries v0.11.0 through v0.15.0 &lt;a href="https://www.helpnetsecurity.com/2026/10/21/ovhcloud-cli-github-malware/"&gt;swapped for a stealer&lt;/a&gt; through a hijacked developer GitHub account. That happened on the 19th and was reported yesterday.&lt;/li&gt;
&lt;li&gt;State Bank of India confirmed 3,897 of its customers’ records turned up in a leaked dataset.&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://safedep.io/testmetest-npm-worm-miasma-variant/"&gt;Miasma-variant npm worm&lt;/a&gt; got caught while still in testing. It runs from &lt;code&gt;binding.gyp&lt;/code&gt;, so there’s no install script to spot, and it wipes your home directory if you revoke the GitHub token it stole. Remove its persistence first, &lt;em&gt;then&lt;/em&gt; rotate.&lt;/li&gt;
&lt;li&gt;The UK ICO reprimanded a Welsh public-sector IT provider whose 2024 ransomware breach started with a 12-year-old test account using the password &lt;code&gt;Password1&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Holy cow. Go patch your stuff, y’all.&lt;/p&gt;</content>
    <author>
      <name>Jake Marsh</name>
    </author>
  </entry>
  <entry>
    <id>tag:deallocatedobjects.com,2005:Post/198</id>
    <published>2015-09-08T11:34:00Z</published>
    <updated>2015-09-13T22:20:12.365Z</updated>
    <link href="https://deallocatedobjects.com/posts/little-bites-of-cocoa" rel="alternate" type="text/html"/>
    <link href="https://deallocatedobjects.com/posts/little-bites-of-cocoa.md" rel="alternate" type="text/markdown"/>
    <category label="Things I Made" term="things-i-made"/>
    <category label="iOS Development" term="ios-development"/>
    <summary>I suppose it’s about time I wrote about Little Bites of Cocoa. I’ve been having a blast with it. Here’s a little about how it came to be: The idea was simple. Daily bite-sized (get it?) tips and tricks to give readers a brief overview or explanation of a particular concept, technique, or tool. I…</summary>
    <title>▸ Little Bites of Cocoa &#127851;</title>
    <content type="html">&lt;p&gt;I suppose it’s about time I wrote about &lt;a href="http://littlebitesofcocoa.com"&gt;&lt;em&gt;Little Bites of Cocoa&lt;/em&gt;&lt;/a&gt;. I’ve been having a blast with it. Here’s a little about how it came to be:&lt;/p&gt;

&lt;p&gt;The idea was simple. Daily bite-sized (&lt;em&gt;get it?&lt;/em&gt;) tips and tricks to give readers a brief overview or explanation of a particular concept, technique, or tool.&lt;/p&gt;

&lt;p&gt;I drew tons of inspiration from some of my favorite publications and content like &lt;a href="https://web.archive.org/web/20150909075653/http://railscasts.com"&gt;Railscasts&lt;/a&gt; and &lt;a href="http://nshipster.com"&gt;NSHipster&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Originally I was planning on making &lt;em&gt;Little Bites of Cocoa&lt;/em&gt; as a series of very short &lt;strong&gt;screencasts&lt;/strong&gt;, but I quickly realized just how much work that was going to be. &lt;strong&gt;I’m lazy.&lt;/strong&gt; I thought there might be a way I could communicate the same information, but without all the effort of video production.&lt;/p&gt;

&lt;p&gt;Around the same time, I was experimenting with what it might be like to publish content &lt;em&gt;“natively”&lt;/em&gt; on &lt;strong&gt;Twitter&lt;/strong&gt;. I’m a big &lt;strong&gt;Twitter&lt;/strong&gt; user, and I knew there was a huge community of &lt;strong&gt;iOS developers&lt;/strong&gt; there. I found an image size that &lt;strong&gt;Twitter&lt;/strong&gt; would display without cropping, and got to work.&lt;/p&gt;

&lt;p&gt;The idea of each &lt;em&gt;“bite”&lt;/em&gt; existing as a simple &lt;strong&gt;image file&lt;/strong&gt; that could be shared around the web seemed very interesting, and I found it to be a very fun medium.&lt;/p&gt;

&lt;p&gt;I composed each of the &lt;strong&gt;images&lt;/strong&gt; by hand in &lt;a href="http://bohemiancoding.com/sketch/"&gt;Sketch&lt;/a&gt; using &lt;a href="https://web.archive.org/web/20150908194108/http://bohemiancoding.com/sketch/support/documentation/07-symbols/"&gt;Symbols&lt;/a&gt; for things like &lt;strong&gt;lower thirds&lt;/strong&gt;. Yes, I still do it this way, even now. It’s the only way I can ensure the quality level. I often make fine-tune adjustments to the content to accomodate for showing it in such a contrained medium.&lt;/p&gt;

&lt;p&gt;The process of simplifying a particular concept or set of steps to fit in the &lt;strong&gt;1024 x 412&lt;/strong&gt; area available in each bite can be extremely challenging, but it’s also very rewarding once I finally ‘crack’ the code. Composing each one becomes a sort of puzzle where I just need to find a way to express the concepts in their purest forms, with as little distraction or ‘fluff’ as possible.&lt;/p&gt;

&lt;p&gt;I created a &lt;strong&gt;Twitter&lt;/strong&gt; account: &lt;a href="http://twitter.com/lilbitesofcocoa"&gt;@lilbitesofcocoa&lt;/a&gt; and began posting my bites. The response was humbling, to say the least. So many awesome reactions and pieces of feedback. The biggest complaint at first was readers who wanted a better &lt;strong&gt;format&lt;/strong&gt; for reading on their phones. The widescreen &lt;strong&gt;images&lt;/strong&gt; just weren’t cutting it for people trying to pinch and zoom their way around each bite. I began converting each bite into &lt;a href="http://daringfireball.net/projects/markdown/"&gt;Markdown&lt;/a&gt; and posting them to a &lt;a href="http://littlebitesofcocoa.tumblr.com"&gt;Tumblr&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Since I now had all the content in &lt;strong&gt;Markdown&lt;/strong&gt;, and by extension &lt;strong&gt;HTML&lt;/strong&gt;, I decided to offer a daily &lt;strong&gt;email&lt;/strong&gt; format as well. I used &lt;a href="http://eepurl.com/bnvVGP"&gt;Mailchimp&lt;/a&gt; to create a &lt;strong&gt;mailing list&lt;/strong&gt; and to my surprise folks started signing up!&lt;/p&gt;

&lt;p&gt;By now, I was having a ton of fun and looking for more places to share the content. I’d been a reader and subscriber of the &lt;a href="https://www.reddit.com/r/iOSProgramming"&gt;/r/iOSProgramming&lt;/a&gt; subreddit for years and thought the community there would appreciate the concise image-based tips. It seems I was correct, the folks there loved it so much that the awesome moderators even &lt;a href="https://www.reddit.com/r/iOSProgramming/search?sort=new&amp;amp;restrict_sr=on&amp;amp;q=flair%3A%F0%9F%8D%AB%2BLBoC"&gt;gave me my own LBoC flair&lt;/a&gt; to use for my posts. Neat!&lt;/p&gt;

&lt;p&gt;I continued publishing bites and eventually began to realize that I was outgrowing the &lt;strong&gt;Tumblr&lt;/strong&gt; account I’d setup to host the ‘web version’ of bites. I decided to create my own site and CMS from scratch. This allowed me to have complete control over the presentation and organization of the content. I was also able to automate a fair amount of the work I was doing by hand.&lt;/p&gt;

&lt;p&gt;It was around this time that I formed a partnership with my great friends over at the &lt;a href="http://spec.fm"&gt;Spec Network&lt;/a&gt;. They were looking to broaden the content they provided and &lt;em&gt;Little Bites&lt;/em&gt; seemed like a great fit. If you haven’t checked out &lt;a href="https://web.archive.org/web/20150907203738/http://spec.fm/podcasts/design-details"&gt;all&lt;/a&gt; &lt;a href="http://spec.fm/podcasts/immutable"&gt;the&lt;/a&gt; &lt;a href="http://spec.fm/podcasts/developer-tea"&gt;great&lt;/a&gt; &lt;a href="https://web.archive.org/web/20150903003647/http://spec.fm/slack"&gt;stuff&lt;/a&gt; over there, you’re missing out.&lt;/p&gt;

&lt;p&gt;Finally: I’m proud to say that as of today, &lt;a href="https://littlebitesofcocoa.com"&gt;the new site is now live&lt;/a&gt;, complete with &lt;a href="https://littlebitesofcocoa.com/campaigns/3/redirect"&gt;the first Sponsor&lt;/a&gt;! &lt;/p&gt;</content>
    <author>
      <name>Jake Marsh</name>
    </author>
  </entry>
  <entry>
    <id>tag:deallocatedobjects.com,2005:Post/197</id>
    <published>2015-06-11T01:06:00Z</published>
    <updated>2015-11-28T09:04:36.438Z</updated>
    <link href="https://deallocatedobjects.com/posts/things-i-learned-at-wwdc-today" rel="alternate" type="text/html"/>
    <link href="https://deallocatedobjects.com/posts/things-i-learned-at-wwdc-today.md" rel="alternate" type="text/markdown"/>
    <category label="WWDC" term="wwdc"/>
    <summary>One of the best kept secrets of WWDC is that random conversations and run-ins as well as the official Labs at the conference are where much of the ticket value lies. You can often learn tons of interesting details just by talking shop with the Apple employees hanging around. Here’s a summary of…</summary>
    <title>Things I Learned at WWDC Today</title>
    <content type="html">&lt;p&gt;One of the best kept secrets of WWDC is that random conversations and run-ins as well as the official Labs at the conference are where much of the ticket value lies.&lt;/p&gt;

&lt;p&gt;You can often learn tons of interesting details just by talking shop with the Apple employees hanging around.&lt;/p&gt;

&lt;p&gt;Here’s a summary of all the interesting details I learned today:&lt;/p&gt;

&lt;h2 id="homekit" data-level="3"&gt;HomeKit&lt;/h2&gt;

&lt;p&gt;The new HomeKit triggers don’t let you run arbitrary code, you schedule things like “when the garage opens, turn the thermostat to 70 degrees”, and then everything happens out of process from your app.&lt;/p&gt;

&lt;p&gt;Notifications about property changes on HomeKit devices are only delivered if your app is in the foreground. Great for privacy, but boo.&lt;/p&gt;

&lt;h2 id="replaykit" data-level="3"&gt;ReplayKit&lt;/h2&gt;

&lt;p&gt;Apple is totally fine with developers using this framework to record anything, not just Games. Wondering what interesting use cases (besides the obvious: user testing). This could get really fun.&lt;/p&gt;

&lt;p&gt;Developers never get access directly to the video files. They’re stored in a system directory and the user is offered the chance to share them with a standard activity view controller once recording completes. You can add custom actions that will appear in the activity view controller that appears though.&lt;/p&gt;

&lt;h2 id="gameplaykit" data-level="3"&gt;GameplayKit&lt;/h2&gt;

&lt;p&gt;Just like ReplayKit, you’re free to use all of the features of GameplayKit for things other than games. Hey look, a free state machine implementation. Thanks Apple! (GameplayKit has a &lt;em&gt;ton&lt;/em&gt; of incredbly useful and awesome features, I was just curious about this particular use case.)&lt;/p&gt;

&lt;h2 id="search" data-level="3"&gt;Search&lt;/h2&gt;

&lt;p&gt;Apple has already been crawling the web and looking at things such as existing meta tags that are commonly included on popular websites. They’re also noticing if sites have the old (but still great) Smart App Banners feature. When possible, they’re tying the information together. If your app is installed, when a user taps on a search result, it deep links you into the app, otherwise, the website and Smart App Banner are shown. &lt;/p&gt;

&lt;p&gt;In fact, this system is so far along that I was actually shown a demo of one of the apps I work on completely working as described above already on iOS 9 (since Apple already crawled the website and the meta tags were already configured correctly.) Neat!&lt;/p&gt;

&lt;h2 id="other" data-level="3"&gt;Other&lt;/h2&gt;

&lt;p&gt;Mango Odwalla is delicious.&lt;/p&gt;</content>
    <author>
      <name>Jake Marsh</name>
    </author>
  </entry>
  <entry>
    <id>tag:deallocatedobjects.com,2005:Post/196</id>
    <published>2015-05-21T23:23:00Z</published>
    <updated>2015-05-22T00:55:22.748Z</updated>
    <link href="https://deallocatedobjects.com/posts/some-interesting-hints-about-ios-9" rel="alternate" type="text/html"/>
    <link href="https://deallocatedobjects.com/posts/some-interesting-hints-about-ios-9.md" rel="alternate" type="text/markdown"/>
    <category label="Apple" term="apple"/>
    <category label="WWDC" term="wwdc"/>
    <summary>Today on Twitter, some clever sleuths discovered some interesting info tucked away in the latest WebKit source code. Specifically, references to iOS 9 and OS X 10.11 were found. Here are some of the highlights: Looks like we’re getting something called “App Links” this year…</summary>
    <title>Some Interesting Hints About iOS 9</title>
    <content type="html">&lt;p&gt;Today on Twitter, some &lt;a href="https://twitter.com/steipete"&gt;clever&lt;/a&gt; &lt;a href="https://twitter.com/PiersonBro"&gt;sleuths&lt;/a&gt; discovered some interesting info tucked away in the latest WebKit source code.&lt;/p&gt;

&lt;p&gt;Specifically, references to iOS 9 and OS X 10.11 were found.&lt;/p&gt;

&lt;p&gt;Here are some of the highlights:&lt;/p&gt;

&lt;blockquote class="twitter-tweet" lang="en"&gt;&lt;p lang="en" dir="ltr"&gt;Looks like we’re getting something called “App Links” this year: &lt;a href="https://t.co/cKKqCnKb4w"&gt;https://t.co/cKKqCnKb4w&lt;/a&gt;&amp;#10;&amp;#10;That should be fun!&amp;#10;&amp;#10;/via &lt;a href="https://twitter.com/PiersonBro"&gt;@piersonbro&lt;/a&gt; &amp;amp; &lt;a href="https://twitter.com/steipete"&gt;@steipete&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jake Marsh (@jakemarsh) &lt;a href="https://twitter.com/jakemarsh/status/601421191065153537"&gt;May 21, 2015&lt;/a&gt;&lt;/blockquote&gt;

&lt;h2 id="app-links" data-level="3"&gt;App Links&lt;/h2&gt;

&lt;p&gt;References such as &lt;code&gt;LSAppLinkOpenCompletionHandler&lt;/code&gt; could point to a few different sorts of things.&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://web.archive.org/web/20201108110033/https://twitter.com/nicklockwood/status/595347585747001344"&gt;fair amount of hub-ub&lt;/a&gt; has been bustling about lately in regards to UIKit’s &lt;code&gt;canOpenURL:&lt;/code&gt; methods and the way some developers were trying to be sneaky and use the API for what it was designed to do. (I kid, I kid). App Links could be Apple revamping the URL handling systems in iOS and OS X to be more “safe” (more restricted to be sure).&lt;/p&gt;

&lt;p&gt;Another idea that comes to mind is something closer to what Facebook and &lt;a href="https://web.archive.org/web/20150604222557/http://applinks.org"&gt;applinks.org&lt;/a&gt; have attempted. Encouraging and supporting more iOS and Mac developers to create  indexable content URLs seems like a smart strategic move on Apple’s part. Plus, with &lt;a href="http://appleinsider.com/articles/15/05/06/apple-challenges-google-with-growing-web-search-program-fueled-by-topsy-acquisition"&gt;all the reports of their recent search efforts&lt;/a&gt;, who knows what sorts of things they could be working on.&lt;/p&gt;

&lt;blockquote class="twitter-tweet" lang="en"&gt;&lt;p lang="en" dir="ltr"&gt;Looks like OS X 10.11 gets Dynamic Type as well. &lt;a href="https://t.co/zCvcNlEVQf"&gt;https://t.co/zCvcNlEVQf&lt;/a&gt;&lt;/p&gt;&amp;mdash; Peter Steinberger (@steipete) &lt;a href="https://twitter.com/steipete/status/601422007100645376"&gt;May 21, 2015&lt;/a&gt;&lt;/blockquote&gt;

&lt;h2 id="dynamic-type" data-level="3"&gt;Dynamic Type&lt;/h2&gt;

&lt;p&gt;More platform feature-parity happening here. &lt;a href="https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/CustomTextProcessing/CustomTextProcessing.html#//apple_ref/doc/uid/TP40009542-CH4-SW65"&gt;Dynamic Type&lt;/a&gt; is a fantastic system and I’m quite happy to see it making its way to the Mac and specifically into WebKit.&lt;/p&gt;

&lt;p&gt;There’s bound to be more undiscovered goodies littered around the WebKit codebase. If you’d like to poke around yourself, &lt;a href="https://github.com/WebKit/webkit/search?q=90000"&gt;here’s a good place to start&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you find anything interesting, &lt;a href="http://twitter.com/jakemarsh"&gt;drop me a line&lt;/a&gt;.&lt;/p&gt;</content>
    <author>
      <name>Jake Marsh</name>
    </author>
  </entry>
  <entry>
    <id>tag:deallocatedobjects.com,2005:Post/195</id>
    <published>2015-03-25T23:02:00Z</published>
    <updated>2015-03-25T23:32:27.231Z</updated>
    <link href="https://deallocatedobjects.com/posts/componentkit" rel="alternate" type="text/html"/>
    <link href="https://deallocatedobjects.com/posts/componentkit.md" rel="alternate" type="text/markdown"/>
    <category label="iOS Development" term="ios-development"/>
    <category label="Open Source" term="open-source"/>
    <summary>ComponentKit takes a functional, declarative approach to building UI. It lets you focus on what your UI should look like, not the steps necessary to build it. It emphasizes a one-way data flow from immutable models to immutable components that describe how views should be configured. It does the…</summary>
    <title>▸ ComponentKit</title>
    <content type="html">&lt;p&gt;&lt;img src="https://deallocatedobjects.com/media/e760a076c42a.png" style="float: right; margin: 20px; margin-top: 40px; width: 159px;" alt="ComponentKit logo: stacked light blue blocks on a teal rounded square" loading="lazy" decoding="async" width="319" height="318"&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ComponentKit takes a functional, declarative approach to building UI. It lets you focus on what your UI should look like, not the steps necessary to build it. It emphasizes a one-way data flow from immutable models to immutable components that describe how views should be configured. It does the heavy lifting of building a view hierarchy from this description.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I’ve been waiting to play around with this library since I saw &lt;a href="https://www.youtube.com/watch?v=mLSeEoC6GjU"&gt;this fantastic talk from Adam Ernst and Ari Grant&lt;/a&gt; late last year.&lt;/p&gt;

&lt;p&gt;There’s many pieces to ComponentKit, but one of the most interesting (to me at the moment) is it’s declarative layout system:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is random code I copy/pasted together to show some examples of layout inside ComponentKit. Please refer to &lt;a href="https://web.archive.org/web/20150328180922/http://componentkit.org/docs/getting-started.html"&gt;their excellent documentation&lt;/a&gt; to see the real thing.&lt;/em&gt;&lt;/p&gt;
&lt;figure class="code" data-lang="objective-c"&gt;&lt;figcaption&gt;&lt;span class="lang"&gt;Objective-C&lt;/span&gt;&lt;/figcaption&gt;&lt;pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"&gt;&lt;code&gt;&lt;span class="line"&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;[CKInsetComponent&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;    newWithInsets:{.top &lt;/span&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt;=&lt;/span&gt;&lt;span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"&gt; 70&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;, .bottom &lt;/span&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt;=&lt;/span&gt;&lt;span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"&gt; 25&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;, .left &lt;/span&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt;=&lt;/span&gt;&lt;span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"&gt; 20&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;, .right &lt;/span&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt;=&lt;/span&gt;&lt;span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"&gt; 20&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;    component:&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;        [CKStackLayoutComponent &lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;            newWithStyle:{&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;                .direction &lt;/span&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt;=&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt; CKStackLayoutComponentDirectionVertical,&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;            } &lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;            children:{&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;                {[HeaderComponent &lt;/span&gt;&lt;span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"&gt;newWithArticle:&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;article]},&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;                {[MessageComponent &lt;/span&gt;&lt;span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"&gt;newWithArticle:&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;article]},&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;                {[FooterComponent &lt;/span&gt;&lt;span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"&gt;newWithArticle:&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;article]},&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;        ]&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;];&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;I’ve been working in WatchKit quite a bit lately, and I’ve come to really enjoy it’s layout system a lot. It’s certainly limiting, but it’s fantastic for quickly putting things together when you have an idea. I’ve been wishing a library would come along that emulated this sort of system for iOS in general, and ComponentKit fits the bill.&lt;/p&gt;

&lt;p&gt;Even though some of the ideas and foundations in ComponentKit clash pretty heavily with UIKit’s philosophies, I’m encouraged by the pure usability of their system. Even their choice of Objective-C++ as a language was made to make the end-resulting code that uses the library as readable as possible.&lt;/p&gt;

&lt;p&gt;It’s certainly not meant to be a catch-all and won’t be the right choice for every app, but in the cases where it makes sense, ComponentKit seems like a slam dunk.&lt;/p&gt;</content>
    <author>
      <name>Jake Marsh</name>
    </author>
  </entry>
  <entry>
    <id>tag:deallocatedobjects.com,2005:Post/194</id>
    <published>2015-03-17T03:12:00Z</published>
    <updated>2015-03-17T03:41:16.370Z</updated>
    <link href="https://deallocatedobjects.com/posts/apple-tv-service-coming-in-june" rel="alternate" type="text/html"/>
    <link href="https://deallocatedobjects.com/posts/apple-tv-service-coming-in-june.md" rel="alternate" type="text/markdown"/>
    <category label="Apple" term="apple"/>
    <summary>Looks like it’s finally happening. The subscription will allegedly cost $30-$40 month, which is a tad pricer than Sling TV’s core $20 package. 25 channels may sound like not that many, but Sling TV’s cable-free subscription only offers 14 with $5 add-on packs like “Kids Extra” and “News &amp; Info…</summary>
    <title>▸ Apple TV Service Coming in June</title>
    <content type="html">&lt;p&gt;&lt;img src="https://deallocatedobjects.com/media/ea57e14ed454.png" style="float: right; width: 196px; margin: 20px;" alt="Hand holding a black second-generation Apple TV box" loading="lazy" decoding="async" width="392" height="394"&gt;&lt;/p&gt;

&lt;p&gt;Looks like it’s finally happening.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The subscription will allegedly cost $30-$40 month, which is a tad pricer than Sling TV’s core $20 package. 25 channels may sound like not that many, but Sling TV’s cable-free subscription only offers 14 with $5 add-on packs like “Kids Extra” and “News &amp;amp; Info Extra.”&lt;br&gt;
&lt;br /&gt;&lt;br&gt;
According to &lt;a href="http://www.wsj.com/articles/apple-in-talks-to-launch-online-tv-service-1426555611"&gt;the Journal&lt;/a&gt;, the likes of ABC, CBS and Fox are lined up as partners with Apple. But NBCUniversal is not on board because Apple has interestedly had a falling out with the network’s parent company, Comcast. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I’ve been &lt;a href="https://deallocatedobjects.com/posts/what-if-apple-made-a-tv"&gt;thinking about Apple’s TV service for long time now&lt;/a&gt;, and this seems about on par with what I was expecting out of a first generation product.&lt;/p&gt;

&lt;p&gt;In the wake of the apparent success of iCloud Photo Library and CloudKit itself, &lt;em&gt;(in that we haven’t heard much about them in the press)&lt;/em&gt; I’m happy to see Apple has gotten back on the proverbial horse when it comes to launching new services.&lt;/p&gt;

&lt;p&gt;Also, I’ve heard similar things about the Apple/Comcast relationship. I’d be surprised if Comcast isn’t already planning to cave a few months after the new device launches.&lt;/p&gt;</content>
    <author>
      <name>Jake Marsh</name>
    </author>
  </entry>
  <entry>
    <id>tag:deallocatedobjects.com,2005:Post/193</id>
    <published>2014-11-19T05:20:00Z</published>
    <updated>2014-11-19T16:36:17.872Z</updated>
    <link href="https://deallocatedobjects.com/posts/confirmed-apple-watch-runs-ios" rel="alternate" type="text/html"/>
    <link href="https://deallocatedobjects.com/posts/confirmed-apple-watch-runs-ios.md" rel="alternate" type="text/markdown"/>
    <category label="Apple" term="apple"/>
    <summary>Today’s release of WatchKit definitely had a few developers scratching their heads. While on the one hand, many of us weren’t expecting as many different integrations points as we got in this first release, the APIs that were announced certainly leave a bit to be desired. Specifically for those of…</summary>
    <title>Confirmed: Apple Watch Runs iOS</title>
    <content type="html">&lt;p&gt;&lt;img src="https://deallocatedobjects.com/media/19c15a01eaa8.png" style="float: right; margin: 20px; width: 132px;" alt="WatchKit logo: a clock face on three stacked red, green and blue rounded squares" loading="lazy" decoding="async" width="265" height="287"&gt;&lt;/p&gt;

&lt;p&gt;Today’s release of WatchKit definitely had a few developers scratching their heads. &lt;/p&gt;

&lt;p&gt;While on the one hand, many of us weren’t expecting as many different integrations points as we got in this first release, the APIs that were announced certainly leave a bit to be desired. Specifically for those of us with aspirations to build full-fledged native apps for the Apple Watch.&lt;/p&gt;

&lt;p&gt;Apple has of course said that “full” support for building “full” apps will come later this year, but some enterprising developers are already starting to poke around and discover what it might be like.&lt;/p&gt;

&lt;p&gt;Many have speculated about what sort of operating system Apple Watch will run, with not much data to pull from. Now that WatchKit has been made (at least partially) public, we seem to have some much more solid evidence for that operating system being iOS.&lt;/p&gt;

&lt;p&gt;Here’s a stream of tweets from &lt;a href="https://twitter.com/stroughtonsmith"&gt;Steve Troughton-Smith&lt;/a&gt; (everyone’s favorite neighborhood tinkerer), as well as &lt;a href="https://twitter.com/andpoul"&gt;Andrew Pouliot&lt;/a&gt; (iOS at Facebook), regarding WatchKit:&lt;/p&gt;

&lt;blockquote class="twitter-tweet" lang="en"&gt;&lt;p&gt;&lt;a href="https://twitter.com/stroughtonsmith"&gt;@stroughtonsmith&lt;/a&gt; Seems all of the WATCH stuff is built on top of UIKit, with some minor additions in PepperUICore. WK-SP-PUI. Cool.&lt;/p&gt;&amp;mdash; Andrew Pouliot (@andpoul) &lt;a href="https://twitter.com/andpoul/status/534895211181518848"&gt;November 19, 2014&lt;/a&gt;&lt;/blockquote&gt;

&lt;blockquote class="twitter-tweet" lang="en"&gt;&lt;p&gt;&lt;a href="https://twitter.com/flyosity"&gt;@flyosity&lt;/a&gt; &lt;a href="https://twitter.com/joeldev"&gt;@joeldev&lt;/a&gt; &lt;a href="https://twitter.com/stroughtonsmith"&gt;@stroughtonsmith&lt;/a&gt; If you want a preview of the APIs, look at PepperUICore. The actual code inherits from UIView et al.&lt;/p&gt;&amp;mdash; Andrew Pouliot (@andpoul) &lt;a href="https://twitter.com/andpoul/status/534920964878766080"&gt;November 19, 2014&lt;/a&gt;&lt;/blockquote&gt;

&lt;blockquote class="twitter-tweet" lang="en"&gt;&lt;p&gt;…if there were any doubt that Apple Watch runs iOS, its UI framework is bundled in the 8.2 SDK, links UIKit: &lt;a href="http://t.co/iaOHkuxple"&gt;http://t.co/iaOHkuxple&lt;/a&gt;&lt;/p&gt;&amp;mdash; Steve T-S (@stroughtonsmith) &lt;a href="https://twitter.com/stroughtonsmith/status/534922363922808832"&gt;November 19, 2014&lt;/a&gt;&lt;/blockquote&gt;

&lt;blockquote class="twitter-tweet" lang="en"&gt;&lt;p&gt;…all the Apple Watch UI elements are subclasses of UIKit classes. It’s all there.&lt;/p&gt;&amp;mdash; Steve T-S (@stroughtonsmith) &lt;a href="https://web.archive.org/web/20150217063754/https://twitter.com/stroughtonsmith/status/534922697822982144"&gt;November 19, 2014&lt;/a&gt;&lt;/blockquote&gt;

&lt;blockquote class="twitter-tweet" lang="en"&gt;&lt;p&gt;If you want a head start on native Apple Watch apps, the private PepperUICore framework lets you build them right now &lt;a href="http://t.co/uBoaVSyfrR"&gt;pic.twitter.com/uBoaVSyfrR&lt;/a&gt;&lt;/p&gt;&amp;mdash; Steve T-S (@stroughtonsmith) &lt;a href="https://twitter.com/stroughtonsmith/status/534935227781873664"&gt;November 19, 2014&lt;/a&gt;&lt;/blockquote&gt;

&lt;blockquote class="twitter-tweet" lang="en"&gt;&lt;p&gt;…not much you can do with them. But they’re just iOS apps like any other. Pop @”PUICApplication” in UIApplicationMain &amp;amp; you get Watch UI&lt;/p&gt;&amp;mdash; Steve T-S (@stroughtonsmith) &lt;a href="https://twitter.com/stroughtonsmith/status/534935592317239296"&gt;November 19, 2014&lt;/a&gt;&lt;/blockquote&gt;

&lt;blockquote class="twitter-tweet" lang="en"&gt;&lt;p&gt;AppleWatch is UIDeviceFamily 4 (where 1 was iPhone, 2 was iPad, and 3 was AppleTV)&lt;/p&gt;&amp;mdash; Steve T-S (@stroughtonsmith) &lt;a href="https://twitter.com/stroughtonsmith/status/534940268102184960"&gt;November 19, 2014&lt;/a&gt;&lt;/blockquote&gt;

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

&lt;p&gt;It seems that the framework powering the more elaborate Apple Watch apps is called &lt;code&gt;PepperUICore&lt;/code&gt; (at least internally). The framework is still private and will likely remain that way until WWDC where I imagine it may gain a much more public-friendly name.&lt;/p&gt;

&lt;p&gt;PepperUICore contains some fun classes like &lt;code&gt;PUICCrownView&lt;/code&gt; and &lt;code&gt;PUICNavigationController&lt;/code&gt;. You can rummage through the &lt;a href="https://web.archive.org/web/20150218172926/https://github.com/nickfrey/WatchOS-Headers/tree/master/PepperUICore.framework"&gt;full private headers here if you’d like&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Also of note are all the other private Apple Watch-related frameworks present in the iOS 8.2 beta. There’s a new &lt;code&gt;Nano&lt;/code&gt; prefix with frameworks that have intriguing names like &lt;a href="https://github.com/nickfrey/WatchOS-Headers/tree/master/NanoLeash.framework"&gt;&lt;code&gt;NanoLeash&lt;/code&gt;&lt;/a&gt; (seems to enable some kind of “find my device” functionality), and &lt;a href="https://github.com/nickfrey/WatchOS-Headers/tree/master/NanoTimeKitCompanion.framework"&gt;&lt;code&gt;NanoTimeKitCompanion&lt;/code&gt;&lt;/a&gt;, which contains references to “monograms” (my guess here is this is the internal name for Watchfaces, and Apple is likely planning some sort of special App Store section for these, there’s a huge market there. Watchfaces will be the new Ringtones).&lt;/p&gt;

&lt;p&gt;For fun I decided to read through some of the &lt;code&gt;PUICTableView&lt;/code&gt; classes and see what sorts of goodies I could find.&lt;/p&gt;

&lt;p&gt;It seems that &lt;code&gt;PUICTableView&lt;/code&gt; (&lt;a href="https://github.com/nickfrey/WatchOS-Headers/blob/master/PepperUICore.framework/PUICTableView.h"&gt;you can view the full file here&lt;/a&gt;) is primarly used as a way to tie together the familiar scrolling workhorse that is &lt;code&gt;UITableView&lt;/code&gt; with the new Digital Crown hardware found on the Apple Watch:&lt;/p&gt;
&lt;figure class="code" data-lang="objective-c"&gt;&lt;figcaption&gt;&lt;span class="lang"&gt;Objective-C&lt;/span&gt;&lt;/figcaption&gt;&lt;pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"&gt;&lt;code&gt;&lt;span class="line"&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt;-&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt; (&lt;/span&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt;void&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;)crownInputSequencer:(&lt;/span&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt;id&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;)arg1 previousDetent:(&lt;/span&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt;id&lt;/span&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt; *&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;)arg2 nextDetent:(&lt;/span&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt;id&lt;/span&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt; *&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;)arg3 forOffset:(&lt;/span&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt;double&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;)arg4;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt;-&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt; (&lt;/span&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt;void&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;)_crownInputSequencerIdleDidChangeNotification:(&lt;/span&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt;id&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;)arg1;&lt;/span&gt;&lt;/span&gt;
&lt;span class="line"&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt;-&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt; (&lt;/span&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt;void&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;)_crownInputSequencerOffsetDidChangeNotification:(&lt;/span&gt;&lt;span style="--shiki-light:#D73A49;--shiki-dark:#F97583"&gt;id&lt;/span&gt;&lt;span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"&gt;)arg1;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;em&gt;&lt;center&gt;sidenote: I love the name ‘detent’ for describing the “steps” along a ‘notched’ wheel like the Digital Crown.&lt;/center&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It seems that the Watch reports itself to be in what’s called &lt;code&gt;UIDeviceFamily&lt;/code&gt; &lt;em&gt;“4”&lt;/em&gt;. iPhone reports &lt;em&gt;“1”&lt;/em&gt;, iPad reports &lt;em&gt;“2”&lt;/em&gt;, and Apple TV reports itself as &lt;em&gt;“3”&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;That’s about as conclusive as it gets and I must say is actually quite reassuring that the Apple Watch is built on the solid foundation of iOS.&lt;/p&gt;

&lt;p&gt;The limitations of many of the APIs in today’s WatchKit release definitely felt like a temporary measure, and I’m happy to see that there’s tons of good stuff in the pipeline, it’s just not ready for public consumption just yet.&lt;/p&gt;

&lt;p&gt;It’s not hard to be reminded of the conversations (before it’s announcement) of the possibility of the iPhone running OS X. (A seemingly unlikely feat at the time).&lt;/p&gt;

&lt;p&gt;But as Apple has done for many years now, they managed to leverage their earlier hard work and solid foundations to leapfrog themselves forward with their next product. OS X’s Foundation, CFNetwork, and countless other battle-tested APIs gave both Apple’s first and third party developers firm starting points to build upon.&lt;/p&gt;

&lt;p&gt;Well that was almost 7 years ago. It seems that iOS (then &lt;em&gt;iPhone OS&lt;/em&gt;) is now all grown up, and ready to help it’s new little brother, the Watch, find success.&lt;/p&gt;</content>
    <author>
      <name>Jake Marsh</name>
    </author>
  </entry>
  <entry>
    <id>tag:deallocatedobjects.com,2005:Post/191</id>
    <published>2014-11-18T19:15:00Z</published>
    <updated>2015-03-17T15:56:33.445Z</updated>
    <link href="https://deallocatedobjects.com/posts/apple-releases-first-beta-of-watchkit" rel="alternate" type="text/html"/>
    <link href="https://deallocatedobjects.com/posts/apple-releases-first-beta-of-watchkit.md" rel="alternate" type="text/markdown"/>
    <category label="iOS Development" term="ios-development"/>
    <category label="Apple" term="apple"/>
    <summary>Apple: WatchKit apps have two parts: A WatchKit extension that runs on iPhone and a set of user interface resources that are installed on Apple Watch. When your app is launched on Apple Watch, the WatchKit extension on iPhone runs in the background to update the user interface and respond to user…</summary>
    <title>▸ Apple Releases First Beta of WatchKit</title>
    <content type="html">&lt;p&gt;Apple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;WatchKit apps have two parts: A WatchKit extension that runs on iPhone and a set of user interface resources that are installed on Apple Watch. When your app is launched on Apple Watch, the WatchKit extension on iPhone runs in the background to update the user interface and respond to user interactions. WatchKit provides three opportunities to extend your iPhone app to Apple Watch: WatchKit apps, Glances, and actionable notifications.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Extremely excited for the opportunities WatchKit and the Apple Watch will open up for third-party developers and users.&lt;/p&gt;

&lt;p&gt;Also nice that we get more than just simple notifications out of the gate. Although it does seem that communication is only one-way for now:&lt;/p&gt;

&lt;blockquote class="twitter-tweet" lang="en"&gt;&lt;p&gt;Write-only &lt;a href="http://t.co/1uaLptfn9B"&gt;pic.twitter.com/1uaLptfn9B&lt;/a&gt;&lt;/p&gt;&amp;mdash; Steve T-S (@stroughtonsmith) &lt;a href="https://twitter.com/stroughtonsmith/status/534788225366511616"&gt;November 18, 2014&lt;/a&gt;&lt;/blockquote&gt;

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

&lt;p&gt;One fun note is that Apple has also released the system font for Apple Watch, called &lt;em&gt;San Francisco&lt;/em&gt;. &lt;a href="http://daringfireball.net/linked/2014/11/18/watchkit-hig"&gt;Gruber points out&lt;/a&gt; another &lt;a href="http://en.wikipedia.org/wiki/San_Francisco_(1984_typeface)"&gt;memorable typeface with that name&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Make sure you check out the &lt;a href="https://web.archive.org/web/20141118200112/https://developer.apple.com/library/prerelease/ios/documentation/UserExperience/Conceptual/WatchHumanInterfaceGuidelines/index.html"&gt;WatchKit Human Interface Guidelines&lt;/a&gt;, the &lt;a href="https://web.archive.org/web/20141120161254/https://developer.apple.com/library/prerelease/ios/documentation/General/Conceptual/WatchKitProgrammingGuide/index.html"&gt;WatchKit Programming Guide&lt;/a&gt; and the &lt;a href="https://developer.apple.com/library/prerelease/ios/documentation/WatchKit/Reference/WatchKit_framework/index.html"&gt;WatchKit Framework Reference&lt;/a&gt;.&lt;/p&gt;</content>
    <author>
      <name>Jake Marsh</name>
    </author>
  </entry>
</feed>