<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://runcode.blog/feed.xml" rel="self" type="application/atom+xml" /><link href="https://runcode.blog/" rel="alternate" type="text/html" /><updated>2026-01-17T22:35:57+00:00</updated><id>https://runcode.blog/feed.xml</id><title type="html">Joseph Roque’s blog</title><subtitle>My daily routine. A blog by Joseph Roque.</subtitle><author><name>Joseph Roque</name></author><entry><title type="html">Ins &amp;amp; Outs for 2026</title><link href="https://runcode.blog/2026/01/13/ins-outs-2026.html" rel="alternate" type="text/html" title="Ins &amp;amp; Outs for 2026" /><published>2026-01-13T03:46:00+00:00</published><updated>2026-01-13T03:46:00+00:00</updated><id>https://runcode.blog/2026/01/13/ins-outs-2026</id><content type="html" xml:base="https://runcode.blog/2026/01/13/ins-outs-2026.html"><![CDATA[<p>Late last year I was invited by a friend to his ‘Ins &amp; Outs’ party for 2026. The basic premise was that everybody would bring a list or powerpoint of what they believed was going to be “In” (trending or worth doing) in 2026, and what, conversely, would be out.</p>

<p>Mostly the party was a fun opportunity to gather, make some cheeky jokes at the expense of ourselves and our friends, and lament on the trends we feared would be in…</p>

<p>Here’s my list of trends. Some are personal, some are a little bit of a reflection on society.</p>

<h2 id="out-cellphones-in-the-bedroom">OUT: Cellphones in the bedroom</h2>

<p>Doomscrolling, distractions – some form of these showed up on many peoples’ lists at the party. My practical version: stop bringing cellphones into the bedroom. Remove the possibility for late night scrolling keeping you up too late, or starting your day with the dread of the state of modern politics.</p>

<p>Practically, this has meant plugging our phones in overnight right outside our bedroom door. We get up, we silence the alarms, and we crawl back into bed for a short snooze. But eventually I do get out of bed earlier than I have been, and my phone isn’t always the first thing I gravitate to anymore, once I am up (but, again, it’s only been a week. Hopefully habits won’t die too hard).</p>

<h2 id="in-buying-flowers-for-no-reason">IN: Buying flowers for no reason</h2>

<p>You don’t need a reason to tell someone you love them; you love them, and that’s enough of a reason in and of itself. Surely, then, we don’t need a reason to <em>express</em> that love in a physical and tangible way that delights and brightens one’s day and space.</p>

<p>In that regard, I would propose that buying flowers for your partners, your dates, your friends, is IN for 2026. You don’t need a reason, you don’t need a corporate holiday marred by overpriced flowers, you just need love in your heart, and a desire to express it.</p>

<p>As a couple, we buy flowers with some regularity, and this more just a reminder to continue doing that, and to perhaps inspire friends to as well.</p>

<h2 id="out-github">OUT: GitHub</h2>

<p>2025 saw a lot of tumultuous activity in the United States, for immigrants, minorities, and the under-privileged, at the behest of the latest administration. Spearheading much of the fearmongering and violence is Immigration and Customs Enforcement (ICE). GitHub, <a href="https://github.blog/news-insights/company-news/github-microsoft/">acquired by Microsoft</a> in 2018, have had their fair share of controversy alongside their parent company, including partnering with <a href="https://www.nbcnews.com/tech/tech-news/we-did-not-sign-develop-weapons-microsoft-workers-protest-480m-n974761">weapons manufacturers</a>. But with the recent role of ICE in the state of affairs in the US, GitHub’s <a href="https://www.vox.com/recode/2019/10/9/20906605/github-ice-contract-immigration-ice-dan-friedman">contract with ICE</a>, and the <a href="https://drewdevault.com/2022/06/23/Copilot-GPL-washing.html">pludering of GPL code for Copilot</a>, it’s time for a change.</p>

<p>This past fall I made the move to <a href="https://sourcehut.org">sourcehut</a>, migrating all of my projects, deleting the code from GitHub, and leaving <a href="https://github.com/autoreleasefool/approach">directions for visitors to my larger projects</a>. <a href="https://sr.ht/~sircmpwn/">Drew DeVault</a>, the founder of sourcehut, has his quirks, but he’s firm on privacy, and the value of free and open-source software.</p>

<p>Sourcehut may not be the right move for you, but I promise you there’s a tool that suits your needs out there:</p>

<ul>
  <li>The <a href="https://ziglang.org/news/migrating-from-github-to-codeberg/">Zig programming language</a> moved to the non-profit <a href="https://codeberg.org">Codeberg</a></li>
  <li><a href="https://tangled.org">Tangled</a> offers decentralized git hosting and collaboration</li>
  <li><a href="https://fossil-scm.org/home/doc/trunk/www/index.wiki">Fossil</a> is a weird one, focused on simplicity and self-hosting</li>
</ul>

<h2 id="in-womens-sports">IN: Women’s Sports</h2>

<p>Women’s sports in North America are seeing a boom. The <a href="https://www.thepwhl.com/en/">Professional Women’s Hockey League</a> had their inaugural season in 2023-2024, and has since expanded from 6 teams to 8, including the Vancouver Goldeneyes. If hockey’s not your sport, the <a href="https://www.nsl.ca">Northern Super League</a> also kicked off this year, a professional women’s soccer league recognized by the Canadian Soccer Association.</p>

<p>The gameplay is elite, the women are pros, and as the two leagues get their footing, tickets are significantly cheaper than any mens’ leagues in the city.</p>

<ul>
  <li>This winter check out the <a href="https://www.thepwhl.com/en/teams/vancouver-goldeneyes/tickets/single-game-tickets">Vancouver Goldeneyes</a> at the Pacific Coliseum on the PNE Fairgrounds.</li>
  <li>When the NSL picks back up in April, find the <a href="https://www.nsl.ca/schedule">Vancouver Rise</a> in Burnaby at Swangard Stadium</li>
</ul>

<h2 id="in-personal-blogging">IN: Personal Blogging</h2>

<p>Everybody should have a blog. Not a tumblr, not an premium X account <a href="https://x.com/Write/status/1646674962055565319">with a 10,000 character limit</a>. The Internet has as much breadth as it has depth, and I implore you to share your own</p>

<p>Publish a blog and let me hear your thoughts. <a href="https://addyosmani.com/blog/write-learn/">Writing pushes you to understand topics better</a>, and has numerous other benefits. Talk about what you love, what you fear. Show me your <a href="/posts/vcbf-2025">poetry</a> or impress me with <a href="/posts/10k-covid-time-trial">your personal progress</a>.</p>

<p>Here are a couple blogs I follow diligently, and some recent posts from them I found interesting. Download an RSS Reader and follow your favourites:</p>

<ul>
  <li><a href="https://mothgoth.substack.com">the writer’s room</a> shares her writing and thoughts on her daily life. Check out her recent <a href="https://mothgoth.substack.com/p/the-flesh-pit">queer cosmic horror short story</a></li>
  <li><a href="https://www.jenn.site">Jenn</a> is a mystery to me, but she writes endearingly about all sorts of things, including tech, culture, and her <a href="https://www.jenn.site/dissolution/">dead, deadbeat gay dad</a></li>
  <li><a href="https://daniellemckeirnan.substack.com/">Dead Moms Book Club</a> writes about her life, her grief, her aspirations. <a href="https://daniellemckeirnan.substack.com/p/another-mothers-day">Another Mother’s Day</a> presents a vulnerability I felt in my core.</li>
</ul>

<h2 id="out-the-algorithm">OUT: The Algorithm</h2>

<p>Jenny Odell, in <em>How to Do Nothing: Resisting the Attention Economy</em> writes about her Twitter feed:</p>

<blockquote>
  <p>Scrolling through the feed, I can’t help but wonder: What am I supposed to think of all this? How am I supposed to think of all this? I imagine different parts of my brain lighting up in a pattern that doesn’t make sense, that forecloses any possible understanding. Many things in there seem important, but the sum total is nonsense, and it produces not understanding but a dull and stupefying dread.</p>
</blockquote>

<p>The Algorithm is everywhere, and it’s designed to capture your attention to commoditize it and sell it to the highest bidder. It’s an unfortunate symptom of much of the free Internet, that driving engagement drives ad sales and boosts revenue, and revenue is king.</p>

<p>In 2026, we are casting out the Algorithm and instead embracing RSS and mailing lists. RSS allows you to follow all the interesting people, blogs, news, etc. without needing to be exposed regularly to the whims of The Algorithm.</p>

<p>I have a feed of bloggers, indie devs, and friends that I have curated and can check in on once a day to see if there’s anything new or of note to me posted by them recently. RSS allows me to aggregate these sources and peruse them simply, at my leisure, without needing to traverse bookmarks or keep a hundred tabs open.</p>

<p>My RSS reader of choice is <a href="https://usetapestry.com">IconFactory’s Tapestry for iOS</a>, which also presents me a chronological feed of my Mastodon timeline.</p>

<p>Find creators, writers, comedians, intellectuals elsewhere. I guarantee you they’re posting somewhere outside the walled gardens of social media, where you can follow them on your own terms.</p>

<p>Joan Westenberg writes a little bit more about <a href="https://www.joanwestenberg.com/big-tech-wants-you-trapped-the-open-web-sets-you-free/">ditching big tech in favour of the open web</a>, and how <a href="https://www.joanwestenberg.com/the-illusion-of-independent-thought/">the algorithm has transformed how we form opinions</a>.</p>

<h2 id="to-a-better-year">To a better year</h2>

<p>Every year is an opportunity to invite self-reflection, and consider what’s important to us. 2026, for me, is going to be about dedicating time to the things I love that bring me joy, and pulling myself away from the platforms and media that don’t.</p>]]></content><author><name>Joseph Roque</name></author><category term="personal" /><summary type="html"><![CDATA[Late last year I was invited by a friend to his ‘Ins &amp; Outs’ party for 2026. The basic premise was that everybody would bring a list or powerpoint of what they believed was going to be “In” (trending or worth doing) in 2026, and what, conversely, would be out.]]></summary></entry><entry><title type="html">Sew Far Sew Good</title><link href="https://runcode.blog/2025/12/21/sew-far-sew-good.html" rel="alternate" type="text/html" title="Sew Far Sew Good" /><published>2025-12-21T21:00:00+00:00</published><updated>2025-12-21T21:00:00+00:00</updated><id>https://runcode.blog/2025/12/21/sew-far-sew-good</id><content type="html" xml:base="https://runcode.blog/2025/12/21/sew-far-sew-good.html"><![CDATA[<p>At the November 2025 edition of the <a href="https://growupgames.ca">Grow UP! Game Jam</a>, I teamed up with a group of 6 others game devs to build <a href="https://nielmclaren.itch.io/sew-far-sew-good">Sew Far Sew Good</a>, my first Game Jam game in over 10 years!</p>

<p><a href="https://growupgames.ca">Grow UP!</a> is an in-person game jam hosted locally in Vancouver, that partners with local non-profits to bring awareness to their causes through game jams. I first heard about it during <a href="https://www.fullindie.com">Full Indie</a>’s annual summit, and attended their November iteration.</p>

<p>For their November iteration, they partnered with 2 non-profits:</p>

<ul>
  <li><a href="https://oursocialfabric.ca">Our Social Fabric</a> is a non-profit fabric store that sells donated fabrics and fibre arts supplies, helping keep ‘waste’ fabric out of the landfill. They’ll sell to anyone with an interest in sustainable sewing supplies, including the fibre arts and slow-fashion communities.</li>
  <li><a href="https://spec.bc.ca/waste/repair-cafe/">SPEC Repair Café</a> is a monthly event hosted by the Society Promoting Environmental Conservation, inviting the public to bring their items in need of repair for free support in mending, repairing, and fixing their items. From bikes, to clothing, to computers, they offer free support in cooperation with the City of Vancouver to help keep waste out of landfills.</li>
</ul>

<p><em>An aside: the City of Vancouver has recently cut funding to the SPEC and various sustainability and arts programs by the way of Ken Sim’s ‘Zero means Zero’ budget cratering funding for a wide array of social services, to support a <a href="https://thetyee.ca/News/2025/11/26/Vancouver-Zero-Means-Zero-Budget-Passes/">zero-percent property tax increase and a $50 million budget increase for the Vancouver Police Department</a>. Let city councillors know <a href="https://vancouver.ca/your-government/tell-us-online.aspx">how much this sucks</a> via their website.</em></p>

<p>A week prior to the event, they announced the theme this time around would be <strong>mending</strong>, and, to me, that immediately screamed “sewing”. The first night of the event, Friday, was focused on brainstorming and team building. We did a group brainstorming project, where everybody was contributing to an ideas board, what exactly “mending” meant to them. Once there were lots of ideas percolating, people started to group up to begin collaborating.</p>

<p>I proposed an idea based on my understanding that fabric scissors, for cutting fabrics and textiles, are fragile and prone to nicks when used to cut things other than their intended use. What if we had a game where the goal was to carefully cut and sew patches over holes, trying to ensure you didn’t damage your scissors, while moving speedily to complete the repair? This idea eventually evolved to be the basis of <a href="https://nielmclaren.itch.io/sew-far-sew-good">Sew Far Sew Good</a>.</p>

<p>I ended up with a team of ~7 in total, including 3 programmers, 2 2D artists, a producer, and an SFX artist. Initially, it sounded a bit like there might be too many cooks, but our devs were either new to gamedev (me) or new to the tooling, meaning we’d be focused on building something fairly small scoped, and hopefully wouldn’t trample on each other’s work too much. Our 2D artists, producer, and SFX artist also found themselves collaborating with multiple teams, or working on their own games on the side, and I think that made for a nice balance, where everybody could find time to contribute to Sew Far Sew Good, and otherwise focus on their own thing.</p>

<p>Overall, the weekend went by quickly, with our team regrouping Saturday morning to kick off game development. By early Saturday, we hadn’t actually fully settled on a game to build… Those of us that arrived earliest started familiarizing ourselves with the tooling of Godot, and just generally working on what we wanted, not fully committed to collaborating. When our producer showed up, they brought up some of the ideas we had had the night before, and suggested we settle on one that seemed approachable, tightly scoped, and fun enough to play. A new Godot project was created, and we were off.</p>

<p>My focus in the game was on the character’s movements. I worked on keyboard controls, as well as programming the animations one of our artists delivered. Looking back, the work was really simple in the end, and the code reflects that. But with it being my first real foray into Godot, it took me most of Saturday to get just a basic character walking animation working!</p>

<p><img src="/assets/posts/sew-far-sew-good-character-process.png" alt="A screenshot of code. A single function called 'process' containing about 25 lines of if-else statements to set an animation variable based on the current state." /></p>

<figcaption>A screenshot of code. A single function called 'process' containing about 25 lines of if-else statements to set an animation variable based on the current state.</figcaption>

<p>This screenshot constitutes most of Saturday’s work! As you can see, besides some other variables being set by signals elsewhere in the code, it isn’t very complex at all. But bumbling through this was a fun challenge perfectly sized for my absolutely beginner Godot skills.</p>

<p><img src="/assets/posts/sew-far-sew-good-cutting.png" alt="A screenshot of the game. A small yellow character struggles to push a large pair of scissors around a heard shaped cutout. A time counts down with 57 seconds remaining." /></p>

<figcaption>A screenshot of the game. A small yellow character struggles to push a large pair of scissors around a heard shaped cutout. A time counts down with 57 seconds remaining.</figcaption>

<p>The team managed to get a solid demo by Saturday night, and most of Sunday was spent refining, and expanding. We added a couple more shapes to our game, we cleaned up the menus, and I worked on an algorithm to generate sewing points around the patches to randomize the gameplay a little bit.</p>

<p><img src="/assets/posts/sew-far-sew-good-sewing.png" alt="A screenshot of the game. The small yellow character stands above a heart shaped patch, with lines representing thread zigzagging between the patch and the background. A timer counts down with 14 seconds remaining." /></p>

<figcaption>A screenshot of the game. The small yellow character stands above a heart shaped patch, with lines representing thread zigzagging between the patch and the background. A timer counts down with 14 seconds remaining.</figcaption>

<p>By the end of Sunday, I’m proud to say, we had a fully playable + complete game! With 3 different shapes, and an overall accuracy report at the end of the game.</p>

<p>Overall, I’m really satisfied with what we accomplished. We managed to scope out a simple game perfectly sized for the weekend and skills that we had as a team. We collaborated effectively, finding things for everybody to do, and helping each other when we got stuck on a tricky bit of code, or tough interaction.</p>

<p>Despite feeling a bit overwhelmed at the start of the weekend, by Sunday I was getting really comfortable with Godot. It’s been an incredible tool, and was so easy to learn thanks to their detailed documentation.</p>

<p>You can play the game <a href="https://nielmclaren.itch.io/sew-far-sew-good">here</a>.</p>]]></content><author><name>Joseph Roque</name></author><category term="game-dev" /><category term="godot" /><category term="gdscript" /><category term="game-jam" /><category term="2d" /><category term="grow-up-games" /><summary type="html"><![CDATA[At the November 2025 edition of the Grow UP! Game Jam, I teamed up with a group of 6 others game devs to build Sew Far Sew Good, my first Game Jam game in over 10 years!]]></summary></entry><entry><title type="html">VCBF: 2025 Haiku Invitational</title><link href="https://runcode.blog/2025/10/30/vcbf-2025.html" rel="alternate" type="text/html" title="VCBF: 2025 Haiku Invitational" /><published>2025-10-30T00:20:00+00:00</published><updated>2025-10-30T00:20:00+00:00</updated><id>https://runcode.blog/2025/10/30/vcbf-2025</id><content type="html" xml:base="https://runcode.blog/2025/10/30/vcbf-2025.html"><![CDATA[<p>The Vancouver Cherry Blossom Festival has <a href="https://vcbf.ca/winning_haiku/2025-winning-haiku/">announced its winners</a> for 2025’s Haiku Invitational. I was excited to see this year I was awarded a “Sakura Award”! Still hoping to win some year 🤞</p>

<h2 id="poem-1-sakura-award">Poem 1 (Sakura Award)</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cherry petals  
find a home in my hair  
where do I belong?
</code></pre></div></div>

<h2 id="poem-2">Poem 2</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Six feet underground
Are the cherry blossoms
Still blooming?
</code></pre></div></div>]]></content><author><name>Joseph Roque</name></author><category term="writing" /><category term="vcbf" /><summary type="html"><![CDATA[The Vancouver Cherry Blossom Festival has announced its winners for 2025’s Haiku Invitational. I was excited to see this year I was awarded a “Sakura Award”! Still hoping to win some year 🤞]]></summary></entry><entry><title type="html">10 Years of Building Approach for Android and iOS</title><link href="https://runcode.blog/2025/04/01/10-years-of-approach.html" rel="alternate" type="text/html" title="10 Years of Building Approach for Android and iOS" /><published>2025-04-01T10:00:00+00:00</published><updated>2025-04-01T10:00:00+00:00</updated><id>https://runcode.blog/2025/04/01/10-years-of-approach</id><content type="html" xml:base="https://runcode.blog/2025/04/01/10-years-of-approach.html"><![CDATA[<p>Exactly 10 years ago I published the first version of my first solo Android app, the 5 Pin Bowling Companion. A lot has changed over the years, and I continue to maintain it today. Multiple total rewrites, a Kotlin migration, and an iOS release, I’m going to talk a little about the journey this app has been through.</p>

<h2 id="why-5-pin-bowling">Why 5-Pin Bowling?</h2>

<p>I started working on the first version of the Bowling Companion (its original name!) in January 2015. I was in my 2nd year of university, and was going to have to start applying to co-op positions for that summer. I was looking for my niche, something that I could build that would be unique, that I had a need for, and that I would be passionate about building. I had been bowling most of my life at that point, and realized that there wasn’t really a good option for 5-Pin Bowling scorekeepers on Android. Actually, I can’t remember if there were any at all! So I set my sights on building the first.</p>

<h2 id="the-5-pin-bowling-companion-the-java-era">The 5-Pin Bowling Companion: The Java Era</h2>

<h3 id="version-one-functional-but-ugly">Version One: Functional, but Ugly</h3>

<p>I built the first version of the app over the course of about a month. I focused primarily on the functionality – things like data storage, score calculations, and statistics. Back then the Bowling Companion was written in Java, with XML, Views, and SQL. Every screen the app contained was a separate Activity, statistics were collected and calculated in an <code class="language-plaintext highlighter-rouge">AsyncTask</code> within the <code class="language-plaintext highlighter-rouge">StatsActivity</code>, and the UI was not quite fleshed out…</p>

<p>One thing that was good enough about this first version though? It helped me stand out among other co-op candidates, and I landed my first internship as an Android developer thanks to being one of the only ones with actual Android experience!</p>

<p><img src="/assets/posts/10-years-of-approach-android-v0.1.png" alt="v0.1 of the 5 Pin Bowling Companion, circa February 2015" /></p>

<figcaption>v0.1 of the 5 Pin Bowling Companion, circa February 2015</figcaption>

<h3 id="version-two-more-functional-less-ugly">Version Two: More Functional, Less Ugly</h3>

<p>Once I finished the first version, and had it functional, I made the most logical decision – scrap it and rebuild it from scratch!</p>

<p>I figured at that point, I had learned so much in the past month, from what APIs were available, to how to better structure my code, that I had already built up so much technical debt, and the performance was likely so poor I could easily make major improvements. So I copied the existing code over to a new folder, and created a new Android template from scratch.</p>

<p>Since the app was mostly working the way I wanted it to at this point, I focused on the design. You can see there are some pretty significant changes in the look of the app from v0.1 to v0.2.</p>

<p><img src="/assets/posts/10-years-of-approach-android-v0.2.png" alt="v0.2 of the 5 Pin Bowling Companion, circa March 2015" /></p>

<figcaption>v0.2 of the 5 Pin Bowling Companion, circa March 2015</figcaption>

<h3 id="version-three-the-initial-release">Version Three: The Initial Release</h3>

<p>Once I had the app looking pretty well how I wanted to, trying out this new “Material” thing Google was toting, I decided that I had, once again, come so far in my learning, it was time to scrap it and start all over once again. This time around, I focused on performance, functionality, and code structure.</p>

<p>I referred to the past implementations a lot, but starting from scratch again allowed me to easily make the changes I needed to, without having to worry about compatibility with what I had already written. Much of the underlying code, like the database, remained the same, but the entire structure of the app changed. I moved from Activities to Fragments, and pulled a lot of the scoring logic out of the activities into utility functions.</p>

<p>This rewrite would eventually become the first version launched, on April 1st, 2015.</p>

<p><img src="/assets/posts/10-years-of-approach-android-v1.0.png" alt="v1.0 of the 5 Pin Bowling Companion, circa April 2015" /></p>

<figcaption>v1.0 of the 5 Pin Bowling Companion, circa April 2015</figcaption>

<h3 id="early-lessons-from-the-early-days">Early Lessons from the Early Days</h3>

<ol>
  <li><strong>Kill your darlings</strong>. I wanted to be proud of this, I wanted to share it with a community I was a part of, and I wanted to provide something useful. The first version of the app, while functional, was not a delight to use! Redesigning it on top of the existing code would probably have taken me much longer, and I might not have gotten that first version out for months, with a lot more technical debt.</li>
  <li><strong>Think about your data model</strong>. The biggest mistake I made in these early days was being far too restrictive with the SQL constraints and datatypes. It made refactors difficult, when I better understood how I should be formatting the data and defining relationships. Some features were impossible for a while until I restructured the database! Taking the time to think about your long-term data model, and leaving yourself some wiggle room in the future can be extremely useful, before you lock yourself into something that becomes impossible to build on top of.</li>
</ol>

<h2 id="rewriting-in-kotlin">Rewriting in Kotlin</h2>

<p>In February 2018, not too long after <a href="https://techcrunch.com/2019/05/07/kotlin-is-now-googles-preferred-language-for-android-app-development/">Google announced</a> official support for Kotlin in Android Studio, I decided to rewrite the Bowling Companion in Kotlin. I experimented with the language a bit before diving in, and found that I really liked a lot of its early ergonomics especially when compared with Java. At this point I just recently graduated and was returning to working at Shopify on their native (at the time) Swift app, and I liked that Kotlin reminded me a lot of Swift.</p>

<p>The rewrite would end up taking me about 7 months, starting in February, with the first Kotlin version being released in September 2018. I don’t fully remember the process, but I do remember that I did not do it component-by-component, instead opting to fully rewrite the app from scratch and convert it all at once. Probably not the best idea, especially since up until this point I had never written a single unit test for the app, but it was hardly the app’s first rewrite! But the Bowling Companion has always first and foremost been a playground for me to explore in, and this was just another exploration.</p>

<p>In writing this blog post, I found the first couple commits, which I think are pretty comical – <a href="https://git.sr.ht/~autoreleasefool/approach/tree/b83a1d5d4f0d3b1dc794f8e94d0be3696acc8fee">the first commit</a>, a complete deletion of the entire Java source code, and <a href="https://git.sr.ht/~autoreleasefool/approach/tree/ebaf0a33bf570fa0f0a6551d3a5350e9351f8ac3">the second commit</a>, a “Hello World!” Kotlin template. Then, in September, the <a href="https://git.sr.ht/~autoreleasefool/approach/tree/72c75cfe55d7610956d250d348c6af1b997eda2e">3.0.0 Release</a> of the Kotlin rewrite.</p>

<p>The months following that release I was plagued with crash reports from <a href="https://www.bugsnag.com/">BugSnag</a>. Mostly <code class="language-plaintext highlighter-rouge">IllegalStateException</code>s, but there was quite a variety. I chased those bugs down and fixed what I could. I don’t have a ton of recollection of this time, I’m mostly just looking through GitHub Issues to jog my memory.</p>

<p><img src="/assets/posts/10-years-of-approach-android-v3.0.png" alt="v3.0 of the 5 Pin Bowling Companion, circa September 2018" /></p>

<figcaption>v3.0 of the 5 Pin Bowling Companion, circa September 2018</figcaption>

<h2 id="taking-a-break">Taking a Break</h2>

<p>In 2020, I ended up mostly taking a break from working on the Bowling Companion. There were few, if any, bug reports or feature requests coming in, and I had picked up some other projects. I was mostly content with where the app was at. I had removed the ads and Google analytics, as I in my mind I didn’t want there to be any burden to maintain it – the app was completely free and detached, and therefore I offered it as-is. I would work on it when I wanted to, on my own terms.</p>

<p>The last version I ended up releasing of this iteration of the app was <code class="language-plaintext highlighter-rouge">3.2.0</code>, in August 2020, targetting SDK 28.</p>

<h2 id="approach-for-ios">Approach for iOS</h2>

<p>In 2022, I started working on the first version of the Bowling Companion for iOS. Mostly I was looking for a new native iOS Swift project to work on, while I was working with React Native in my job. I had recently learned about <a href="https://github.com/pointfreeco/swift-composable-architecture">The Composable Architecture</a>, had some experience building with SwiftUI, and decided to go all in on both for this app.</p>

<p>With how old the Android app had become at this point, I knew I wanted to match its featureset, but I wouldn’t be using its design for any inspiration. I relied on SwiftUI’s default styling primarily, and built an all new UI for the app. This also gave me a chance to rethink the structure of the app from a UX perspective, moving some content to top-level tabs, but leaving the hierarchical nature of Bowlers &gt; Leagues &gt; Series &gt; Games as the primary way users would interact with the app.</p>

<p>Overall, the Bowling Companion for iOS would take me just under a year to build. It wasn’t until March of 2023 that I settled on the name “Approach”, and the first version of the iOS app would release in September 2023.</p>

<p><img src="/assets/posts/10-years-of-approach-ios-v1.0.png" alt="v1.0 of Approach for iOS, circa September 2023" /></p>

<figcaption>v1.0 of Approach for iOS, circa September 2023</figcaption>

<h2 id="jetpack-compose-and-building-approach-for-android">Jetpack Compose and building Approach for Android</h2>

<p>During the process of building Approach for iOS, Google announced that they would be removing unsupported apps from the Play Store, and any app that wasn’t targeting at least SDK 32 (<em>I think</em>) would become unavailable to new users. Of course, as mentioned above, the Bowling Companion only targeted SDK 28, and quite a bit had happened in the ~4 years since its release.</p>

<p>Some of the major hurdles I encountered when thinking about how to address this were:</p>

<ul>
  <li>Kotlin, and especially Kotlin’s Coroutines, had undergone a major version update with breaking changes</li>
  <li>Views were no longer the default recommended by Google, and Jetpack Compose was now preferred</li>
  <li>The Charts library I was using, <a href="https://github.com/PhilJay/MPAndroidChart">MPAndroidChart</a> had not been updated in some time and was likely abandoned.</li>
</ul>

<p>Once again, I would lean on my trusty tool, <strong>the rewrite</strong>. This would now be the SIXTH time I had written the Bowling Companion from scratch, leaning on years of lessons and inspiration, to create what I think is by and far the best version of the app that’s ever existed.</p>

<p>This rewrite ended up taking just over a year to complete, and Approach for Android was first released in September 2024, with feature parity to the iOS app!</p>

<p><img src="/assets/posts/10-years-of-approach-android-v4.0.png" alt="v4.0 of Approach for Android, circa September 2024" /></p>

<figcaption>v4.0 of Approach for Android, circa September 2024</figcaption>

<h2 id="current-state-of-affairs">Current State of Affairs</h2>

<p>As of writing (April 2025), Approach is two native apps, for iOS and Android. Features are generally built in parity, and released in unison!</p>

<p>The iOS app is built with <a href="https://github.com/pointfreeco/swift-composable-architecture">TCA</a> and <a href="https://developer.apple.com/xcode/swiftui/">SwiftUI</a>. It supports iOS 17 and 18. There are 138 modules, including 36 feature modules, 15 repositories, 19 services, and 34 libraries.</p>

<p>The Android app is built with <a href="https://developer.android.com/compose">Jetpack Compose</a>, MVVM, and follows Google’s recommended (as of 2025) layered architecture. It supports SDKs 26 and higher, and targets SDK 35. There are 92 modules, including 20 core modules, 36 feature modules, and 36 feature UI modules.</p>

<p>In total, in the <a href="https://git.sr.ht/~autoreleasefool/approach">repository</a> there have been 3,986 commits to <code class="language-plaintext highlighter-rouge">main</code>, averaging just over 1 commit per day! From a clean clone, <a href="https://github.com/AlDanial/cloc">cloc</a> reports 65,526 lines of Swift across 902 files and 66,347 lines of Kotlin across 901 files. In total, it reports 161,776 lines of “code”, but that includes SVGs, some generated localizations, and some template files.</p>

<p><img src="/assets/posts/10-years-of-approach-languages.png" alt="Approximate percentage of the codebase which is Swift vs Kotlin. Swift shows 49.9%, Kotlin is 49.3%, and &quot;Other&quot; is 0.8%" /></p>

<figcaption>Approximate percentage of the codebase which is Swift vs Kotlin. Swift shows 49.9%, Kotlin is 49.3%, and "Other" is 0.8%</figcaption>

<p>See you in another 10 years! Maybe I’ll be talking about <a href="https://android-developers.googleblog.com/2024/05/android-support-for-kotlin-multiplatform-to-share-business-logic-across-mobile-web-server-desktop.html">Kotlin Multiplatform</a>, <a href="https://skip.tools/">Skip</a>, or something as of yet unreleased!</p>]]></content><author><name>Joseph Roque</name></author><category term="approach" /><category term="ios" /><category term="android" /><category term="bowling" /><summary type="html"><![CDATA[Exactly 10 years ago I published the first version of my first solo Android app, the 5 Pin Bowling Companion. A lot has changed over the years, and I continue to maintain it today. Multiple total rewrites, a Kotlin migration, and an iOS release, I’m going to talk a little about the journey this app has been through.]]></summary></entry><entry><title type="html">Development Log, January 2025</title><link href="https://runcode.blog/2025/02/19/dev-log-2025-01.html" rel="alternate" type="text/html" title="Development Log, January 2025" /><published>2025-02-19T08:00:00+00:00</published><updated>2025-02-19T08:00:00+00:00</updated><id>https://runcode.blog/2025/02/19/dev-log-2025-01</id><content type="html" xml:base="https://runcode.blog/2025/02/19/dev-log-2025-01.html"><![CDATA[<p>December was a month of coding challenges, as I took on <a href="https://adventofcode.com/">Advent of Code</a>, and January was a slow month of vacationing in Uruguay, then reintegrating into the real world.</p>

<h2 id="advent-of-code">Advent of Code</h2>

<p>Every year since its inception I’ve participated in <a href="https://adventofcode.com/">Advent of Code</a>, an annual advent calendar of coding challenges that get released throughout the month of December, with the eventual goal of completing each day and saving Christmas.</p>

<p>Challenges are released each night at 12AM Eastern Time, meaning that I get them at a cool 9PM Pacific. Most nights I hope on a call with some friends and we chat about past challenges and when we’ll encounter certain theories or formulas that have been the hardest challenges in past years.</p>

<p>This year, I ended up with a total of 40/50 stars (challenges solved) at the end of December.</p>

<p>Of all the challenges this year, I think the one I had the most fun with was Day 14, simulating the movement of robots, and finding when they arrange themselves in a Christmas tree shape. You can find my code <a href="https://git.sr.ht/~autoreleasefool/advent-of-code/tree/main/item/2024/day_14/swift/Day14.swift">here</a> which was to basically just continue the simulation and search for a bunch of robots lining up in a row. How I actually solved it realtime though was printing every state of the simulation to my terminal and just using Cmd+F to search for an unlikely number of robots in a single row – I think it was ~10 or so, and it took me straight to the state that output the tree! I ended up 324th overall that day, one of my best performances this year.</p>

<h2 id="hot-chocolate-fest">Hot Chocolate Fest</h2>

<p>Every January in Vancouver Hot Chocolate Fest begins. Their website is always a little difficult to navigate, and it’s never easy to find nearby participating cafés with good flavours. So this year I gave myself a fun little challenge – 3 days to build a better app than they had. <a href="https://git.sr.ht/~autoreleasefool/hot-chocolate-fest">Here’s the final result</a>.</p>

<p>First, scraping the data. The website is annoying to navigate and, so too, was the data source. I ended up scraping 2 pages – the first for the vendor names and details of the hot chocolates they were selling, and the second for the vendor locations from a custom Google Map.</p>

<p>With the data scraped and cleaned up into convenient JSONs for me to read, I began building an interface. I chose SwiftUI simply because I have familiarity with it, it’s quick for bootstrapping, and I wanted this done fast. I also chose SwiftData for the little data I wanted to store alongside the scraped data. This ended up being a mistake – I’ve never used SwiftData personally, and it wasn’t intuitive to map the data I had to the storage format I wanted/SwiftData expected. In the end, I switched over to Pointfree’s <a href="https://github.com/pointfreeco/swift-sharing">Sharing</a> library, which simplified things a lot.</p>

<p>The end result was a simple little app that listed all the vendors and flavours, with a search by name, or a filter by allergens, as well as a map with details of the vendors and offerings. Unfortunately, Hot Chocolate Fest only lasts 1 month and I started this project halfway into the event. Hopefully I can dust it off next January and use it then!</p>]]></content><author><name>Joseph Roque</name></author><category term="dev-log" /><category term="swift" /><category term="programming" /><category term="advent-of-code" /><summary type="html"><![CDATA[December was a month of coding challenges, as I took on Advent of Code, and January was a slow month of vacationing in Uruguay, then reintegrating into the real world.]]></summary></entry><entry><title type="html">Development Log, November 2024</title><link href="https://runcode.blog/2024/12/03/dev-log-2024-11.html" rel="alternate" type="text/html" title="Development Log, November 2024" /><published>2024-12-03T08:00:00+00:00</published><updated>2024-12-03T08:00:00+00:00</updated><id>https://runcode.blog/2024/12/03/dev-log-2024-11</id><content type="html" xml:base="https://runcode.blog/2024/12/03/dev-log-2024-11.html"><![CDATA[<p>This month I wrote a meager 4,251 words for <a href="https://nanowrimo.org/">NaNoWriMo</a>, added a few new statistics to the Approach app, and started prepping for <a href="https://adventofcode.com/">Advent of Code</a>.</p>

<h2 id="nanowrimo">NaNoWriMo</h2>

<p>As I do every year, I attempted to complete NaNoWriMo this November. The goal of NaNoWrimo (National Novel Writing Month) is to write 50,000 words over the course of a month, averaging 1,667 words per day, on one’s way to finishing the first draft of a novel.</p>

<p>I’ve always loved writing, though I generally stick to much more short form writing, but I still aspire to write a novel someday. NaNoWriMo humbles me every year, as I realize just how much of a commitment it is, and, more than that, how hard it is to write something good!</p>

<p>I do enjoy the few writing sessions I get out of it each year though. I generally have some semblance of an idea going in, and then after a few days I lose interest in that idea and move on. I’m still searching for the novel idea that’ll make me want to keep writing.</p>

<h2 id="approach">Approach</h2>

<p><a href="https://tryapproach.app">Approach</a> is the 5 Pin Bowling app I build for iOS and Android. This month I didn’t spend to much time on it, instead preferring to focus on Advent of Code, but I did finish a few small tasks.</p>

<h3 id="adding-high-series-of-x-statistics">Adding High Series of X Statistics</h3>

<p>One of the most useful things about Approach is the statistics it offers, reporting on a variety of aspects about your bowling and you’re improving or where you need to focus your game. These statistics can be charted as a series of points, or accumulated over time to a singular value.</p>

<p>Approach has a system for calculating the statistics, which queries the user’s games, and passes the games to a set of objects which perform calculations based on the properties of the game, and return the final statistic values. This makes it fairly quick and simple to add new statistics into the app.</p>

<p>This month, a user noted that there’s a “High Series of 3” statistic, tracking the highest total series the user has bowled for a series of 3 games. But often leagues are 4 or even more games, and there was no way to measure improvements in those leagues.</p>

<p>Therefore, this month I added a number of statistics for “High Series” of 2 up to 20 games.</p>

<h2 id="advent-of-code">Advent of Code</h2>

<p>December 1st marks the beginning of <a href="https://adventofcode.com/">Advent of Code</a>, a month-long coding challenge that releases a new puzzle at midnight EST each night, where coders all over the world attempt to parse the puzzle and determine a solution as quickly as possible.</p>

<p>I’ve been doing Advent of Code since it first launched in 2015, and I’ve built up a small library of helpful utilities over those years. However, they’re all written in Python because I used to use Python a lot more back in those days, for work or personal projects. But I rarely write Python at all anymore. In fact, the last time I wrote any Python was actually during last year’s Advent of Code. I’ve found that it leads to me spending too much time remembering the intricacies of Python’s list comprehension, or the syntax and tools of my own library.</p>

<p>This year, I’ve decided to attempt to solve all the days using Swift, my preferred language of choice. I spent some time in November converting my personal CLI tool for running the challenges from Python to Swift, and have also converted a number of my personal utilities over to Swift as well.</p>

<p>I post all my Advent of Code <a href="https://git.sr.ht/~autoreleasefool/advent-of-code">solutions</a> on GitHub, and this year will be no different!</p>

<p>I’ll have more of a write-up on how it goes at the end of the month!</p>]]></content><author><name>Joseph Roque</name></author><category term="dev-log" /><category term="approach" /><category term="ios" /><category term="swift" /><category term="programming" /><category term="advent-of-code" /><category term="writing" /><summary type="html"><![CDATA[This month I wrote a meager 4,251 words for NaNoWriMo, added a few new statistics to the Approach app, and started prepping for Advent of Code.]]></summary></entry><entry><title type="html">Development Log, October 2024</title><link href="https://runcode.blog/2024/10/31/dev-log-2024-10.html" rel="alternate" type="text/html" title="Development Log, October 2024" /><published>2024-10-31T17:00:00+00:00</published><updated>2024-10-31T17:00:00+00:00</updated><id>https://runcode.blog/2024/10/31/dev-log-2024-10</id><content type="html" xml:base="https://runcode.blog/2024/10/31/dev-log-2024-10.html"><![CDATA[<p>This month I attended the Vancouver Writer’s Fest in anticipation of NaNoWriMo, and built some data management tooling for Approach.</p>

<h2 id="vancouver-writers-fest">Vancouver Writer’s Fest</h2>

<p>This month was Vancouver’s annual Writer’s Fest, and I finally decided to attend some of the events. I always take a look at the festival’s events and am never really in too much of an active writing mood, but this year I decided to make sure I made it more of a priority and to go to at least one event.</p>

<p>In the end, I actually ended up going to 4!</p>

<p>The most useful was, I think, <strong>Night Class: A Workshop with UBC School of Creative Writing</strong>, where various UBC professors of their creative writing department came to give very condensed lectures and then a short writing prompt. My biggest takeaway from this event was one of the professors (I think it was Whitney French, though I might be misremembering) talked about for her to really get into writing, she had to figure out how her brain worked. For me, I definitely have trouble choosing and focusing on a single task. What works for me when I’m coding is that I let myself pick up and drop tasks very quickly – short, little bursts of intense focus, and then drop it and switch to something new, until I find something that really catches me and I can dedicate time and deep focus to it. Sometimes I have to pick up and drop the same thing multiple times before I can really take off with it. I’ve never applied this style of thinking and working to my writing. I always try to pick one project and see it through for a while before I drop it and never return to it, usually even dropping writing altogether for weeks or months at a time. I’m going to try some different approaches this November to see if I can coax some better writing out of myself this month.</p>

<h2 id="nanowrimo">NaNoWriMo</h2>

<p>Speaking of November, <a href="https://nanowrimo.org/">NaNoWriMo</a> is kicking off next month, an annual challenge to write 50,000 words and finish the first draft of a novel. It’s much less about writing anything of substance, and mostly just about volume – writing and building the habit of writing!</p>

<p>I’ll be doing little to no coding for the next month (if I can keep myself honest…) and I’ll be focused on writing instead.</p>

<h2 id="approach">Approach</h2>

<p><a href="https://tryapproach.app">Approach</a> is the 5 Pin Bowling app I build for iOS and Android. This month I focused on iOS and data management.</p>

<h3 id="automatic-backups">Automatic Backups</h3>

<p>Approach for iOS uses <a href="https://github.com/groue/GRDB.swift">GRDB</a>, a framework for working with SQLite from Swift that makes it really simple to query your SQLite DB with strong typing and robust error handling and concurrency. Perfect for Swift.</p>

<p>Unfortunately, one issue I’ve had with GRDB is figuring out how to safely and effectively sync the database to iCloud. Approach doesn’t charge any money or have a premium plan (yet) and I’m not quite willing to front the costs for a fully fledged backend sync, so it’s gone without any sort of cloud syncing up until now. There’s an option for users to export their data to keep it safe, but I’m not sure you can rely on non-technical users to do that (especially the aging demographic of bowlers).</p>

<p>My solution? I eventually realized that SQLite conveniently works out of a single file. All the data is stored there, and we can move or copy that file easily to create a backup! So all I had to do is use GRDB’s built in <a href="https://github.com/groue/GRDB.swift/tree/master?tab=readme-ov-file#backup">backup</a> command to copy the database to another file, then I could sync that file to a folder in the user’s iCloud (as long as they’re signed into an Apple account).</p>

<p><img src="/assets/posts/devlog-2024-10-approach-ios_backups.png" alt="Screenshot of an iPhone. The title of the screen is &quot;Backups&quot;. There is a section describing backups, a toggle to enable or disable them, and a list of recent backup dates." /></p>

<figcaption>Screenshot of an iPhone. The title of the screen is "Backups". There is a section describing backups, a toggle to enable or disable them, and a list of recent backup dates.</figcaption>

<p>Backup syncing will be enabled by default, but users will be able to disable it and delete the files from their iCloud if they can’t spare the room. It’ll backup with some regularity, and we’ll make sure the backups don’t get too stale or take up too much room. That’s all still a work in progress, but coming soon!</p>

<h3 id="imports-from-android">Imports from Android</h3>

<p>I had a user reach out mid-October and ask why they couldn’t get their data to import correctly from the exported file from their Samsung phone, onto their new iPhone. The reason was that I had never actually built support for this! Let’s understand why this was the case, and how I built support for it.</p>

<h4 id="ios-data-format">iOS Data Format</h4>

<p>When using GRDB’s automatic encoding and decoding for records, column names end up matching exactly how you define the corresponding variable (for example, a League belongs to a Bowler through the <code class="language-plaintext highlighter-rouge">bowlerId</code> foreign key, which is GRDB will write the column name). You can overwrite this default by explicitly defining <code class="language-plaintext highlighter-rouge">CodingKeys</code> on a <code class="language-plaintext highlighter-rouge">Codable</code> record, but I opted to use the default behaviour.</p>

<p><img src="/assets/posts/devlog-2024-10-approach-ios_league_column_names.png" alt="Screenshot of two adjacent windows. The first has a Swift struct definition, League, and a list of properties. The bowlerId property is highlighted. The second has a SQLite table definition. The bowlerId column name is highlighted" /></p>

<figcaption>Screenshot of two adjacent windows. The first has a Swift struct definition, League, and a list of properties. The bowlerId property is highlighted. The second has a SQLite table definition. The bowlerId column name is highlighted</figcaption>

<p>Another default, <code class="language-plaintext highlighter-rouge">enum</code>s are encoded as their String representation, so the enum <code class="language-plaintext highlighter-rouge">League.ExcludeFromStatistics</code> has two cases, <code class="language-plaintext highlighter-rouge">include</code> and <code class="language-plaintext highlighter-rouge">exclude</code> and the SQLite values reflect that:</p>

<p><img src="/assets/posts/devlog-2024-10-approach-ios_enum_values.png" alt="Screenshot of two adjacent windows. The first has a Swift enum definition, Recurrence, and has two values, 'repeating' and 'once'. The values are highlighted. The second has a SQLite table definition. The recurrenc column values matching the enum values are highlighted" /></p>

<figcaption>Screenshot of two adjacent windows. The first has a Swift enum definition, Recurrence, and has two values, 'repeating' and 'once'. The values are highlighted. The second has a SQLite table definition. The recurrenc column values matching the enum values are highlighted</figcaption>

<h4 id="android-data-format">Android Data Format</h4>

<p>Approach for Android uses <a href="https://developer.android.com/training/data-storage/room">Room</a>, the de facto data storage solution for the platform. It has slightly different defaults.</p>

<p>When defining your models, you can easily override the column names directly inline, otherwise they will match your variable definition. This led me to mix the Kotlin standard camelCase with SQLite standard snake_case names, appropriate for each platform. A League’s <code class="language-plaintext highlighter-rouge">bowlerId</code> property in the Kotlin model would be reflected as <code class="language-plaintext highlighter-rouge">bowler_id</code> in the SQLite table column:</p>

<p><img src="/assets/posts/devlog-2024-10-approach-android_league_column_names.png" alt="Screenshot of two adjacent windows. The first has a Kotlin data class definition, League, and a list of properties. The bowlerId property is highlighted. The second has a SQLite table definition. The bowler_id column name is highlighted" /></p>

<figcaption>Screenshot of two adjacent windows. The first has a Kotlin data class definition, League, and a list of properties. The bowlerId property is highlighted. The second has a SQLite table definition. The bowler_id column name is highlighted</figcaption>

<p>In Kotlin, enum class values are usually defined in UPPERCASE, and the value stored in SQLite reflects that:</p>

<p><img src="/assets/posts/devlog-2024-10-approach-android_enum_values.png" alt="Screenshot of two adjacent windows. The first has a Kotlin enum class definition, Recurrence, and has two values, 'REPEATING' and 'ONCE'. The values are highlighted. The second has a SQLite table definition. The recurrenc column values matching the enum values are highlighted" /></p>

<figcaption>Screenshot of two adjacent windows. The first has a Kotlin enum class definition, Recurrence, and has two values, 'REPEATING' and 'ONCE'. The values are highlighted. The second has a SQLite table definition. The recurrenc column values matching the enum values are highlighted</figcaption>

<h4 id="supporting-imports">Supporting Imports</h4>

<p>With these platform differences in mind (among others), I couldn’t simply swap out the underlying DB file when importing, so I had to take a slightly custom approach.</p>

<p>First, I let the user pick the file from their filesystem. iOS will give you a URL as a result, and I start by copying that file to a temporary location (that I clean up at the end of the process) so the system can’t lock my access or delete it out from underneath me while I’m trying to process the file.</p>

<p>Next, I look at the <a href="https://git.sr.ht/~autoreleasefool/approach/tree/main/item/ios/Approach/Sources/ImportExportService/Models/FileType.swift">file extensions and headers</a> and try to determine what sort of file is being imported. When exporting, Approach for iOS uses GRDB’s <a href="https://github.com/groue/GRDB.swift/tree/master?tab=readme-ov-file#backup">backup</a> function to cleanly write to a fresh SQLite DB File. Room doesn’t expose this as easily, so instead I zip up the <code class="language-plaintext highlighter-rouge">sqlite</code> and <code class="language-plaintext highlighter-rouge">sqlite-wal</code> files and give the user a single zip file. That means when importing I need to know if I’m dealing with a SQLite file or a ZIP file and act accordingly. Other files are unsupported and the app will report an error to the user.</p>

<p>Once I have the primary SQLite file, I open it with GRDB and try to <a href="https://git.sr.ht/~autoreleasefool/approach/tree/main/item/ios/Approach/Sources/ImportExportService/Models/DatabaseFormat.swift">perform a few reads</a> to see if I can determine the format. GRDB uses migrations and a <code class="language-plaintext highlighter-rouge">grdb_migrations</code> table to track which migrations have been applied. I know that I can import any Approach for iOS file that has no newer migrations than the current app has – any older and GRDB will automatically handle the migration, any newer and the database is in a format we can’t understand. <a href="https://git.sr.ht/~autoreleasefool/approach/tree/main/item/android/core/database/schemas/ca.josephroque.bowlingcompanion.core.database.ApproachDatabase/5.json">Room uses a hash value</a> to define its DB versions, so I can inspect those.</p>

<p>For any supported iOS DB versions, I just <a href="https://git.sr.ht/~autoreleasefool/approach/tree/main/item/ios/Approach/Sources/ImportExportService/Importing/IOSApproachSQLiteImporter.swift">copy the file</a> to Approach for iOS’s DB file location and let GRDB handle the import/migration. For supported Android DB versions, I perform a series of queries and writes to ensure all the data gets copied over correctly. These queries will have to be kept up to date as the database format evolves over time, but that’s a trade-off I was okay with!</p>

<p>Once the DB file has been prepared, we attempt to open it and if it all succeeds, we let the user inspect the app and validate everything imported correctly – there is always an option to revert.</p>]]></content><author><name>Joseph Roque</name></author><category term="dev-log" /><category term="approach" /><category term="ios" /><category term="swift" /><category term="programming" /><category term="writing" /><summary type="html"><![CDATA[This month I attended the Vancouver Writer’s Fest in anticipation of NaNoWriMo, and built some data management tooling for Approach.]]></summary></entry><entry><title type="html">VCBF: 2024 Haiku Invitational</title><link href="https://runcode.blog/2024/10/22/vcbf-2024.html" rel="alternate" type="text/html" title="VCBF: 2024 Haiku Invitational" /><published>2024-10-22T05:00:00+00:00</published><updated>2024-10-22T05:00:00+00:00</updated><id>https://runcode.blog/2024/10/22/vcbf-2024</id><content type="html" xml:base="https://runcode.blog/2024/10/22/vcbf-2024.html"><![CDATA[<p>This year’s Haiku Invitational for the Vancouver Cherry Blossom Festival has <a href="https://vcbf.ca/winning_haiku/2024-winning-haiku/">announced its winners</a>.</p>

<p>Unfortunately, I didn’t make the cut this year.</p>

<p>Instead, I share my poems with you:</p>

<h2 id="poem-1">Poem 1</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"Your time will come" they say
as if we couldn't be
cherry blossoms in march
</code></pre></div></div>

<h2 id="poem-2">Poem 2</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Children dying, crying
for mothers. Don't they know
the cherries are blossoming
</code></pre></div></div>]]></content><author><name>Joseph Roque</name></author><category term="writing" /><category term="vcbf" /><summary type="html"><![CDATA[This year’s Haiku Invitational for the Vancouver Cherry Blossom Festival has announced its winners.]]></summary></entry><entry><title type="html">Development Log, September 2024</title><link href="https://runcode.blog/2024/09/30/dev-log-2024-09.html" rel="alternate" type="text/html" title="Development Log, September 2024" /><published>2024-09-30T17:00:00+00:00</published><updated>2024-09-30T17:00:00+00:00</updated><id>https://runcode.blog/2024/09/30/dev-log-2024-09</id><content type="html" xml:base="https://runcode.blog/2024/09/30/dev-log-2024-09.html"><![CDATA[<p>Recently I saw <a href="https://www.patreon.com/posts/september-2024-113011369">someone else’s</a> dev log and I thought starting my own could be a good learning and reflection opportunity.</p>

<p>Hopefully this will be a monthly thing. Lots of development happened this month, so there’s a few things to write about. Expect shorter entries in the future.</p>

<h2 id="approach">Approach</h2>

<p><a href="https://tryapproach.app">Approach</a> is the app I built for iOS and Android. I spent a lot of this month focused on the Android version, rebuilding a feature that had been cut as part of my rewrite to Jetpack Compose over the past year.</p>

<h3 id="teams">Teams</h3>

<p>Originally built in, I think, 2016, as a way for people to record scores for more than 1 person at a time, I opted to cut Teams from the MVP for the 4.0.0 release of the app this past August. Instead, I made it possible to pick multiple bowlers at once from the home screen and start recording scores for them. But the feature was half-baked – users complained they weren’t sure how to see scores for more than one bowler at a time, they made mistakes recording and it was unclear how to go back to previous bowlers, and mostly they just didn’t know the feature existed. Discoverability is hard.</p>

<p>Throughout August and September I completely rewrote the Teams functionality, returning it to its former glory as a top-level Tab in the Overview.</p>

<p><img src="/assets/posts/devlog-2024-09-approach-teams.png" alt="Screenshot of an Android device. The app Approach is open, with two tabs, Bowlers and Teams. Teams is selected. There is a list of teams, Worsties and Besties." /></p>

<figcaption>Screenshot of an Android device. The app Approach is open, with two tabs, Bowlers and Teams. Teams is selected. There is a list of teams, Worsties and Besties.</figcaption>

<p>Users can now create teams by selecting any number of bowlers, and record a new league series or tournament for all members of the team simultaneously. Teams also now have their own stats, for all the series the user’s recorded under the series.</p>

<p><img src="/assets/posts/devlog-2024-09-approach-scores.png" alt="Screenshot of an Android device. The app Approach is open, showing a list of names of bowlers and their scores. Joseph has a score of zero. Sarah also has a score of zero." /></p>

<figcaption>Screenshot of an Android device. The app Approach is open, showing a list of names of bowlers and their scores. Joseph has a score of zero. Sarah also has a score of zero.</figcaption>

<h3 id="swift-6">Swift 6</h3>

<p>For the iOS version of Approach, I made 2 overall improvements. First, I migrated the app fully to Swift 6. Mostly this was made incredibly easy with the work done by the <a href="https://pointfree.co">Pointfreeco team</a> and their migration of <a href="https://github.com/pointfreeco/swift-composable-architecture">The Composable Architecture</a>, which the iOS version uses.</p>

<h3 id="halloween-icons">Halloween Icons</h3>

<p>Finally, as a treat for the iOS users (and a trick for the Android users, who bought phones that don’t support custom icons), I designed 3 new app icons:</p>

<p><img src="/assets/posts/devlog-2024-09-halloween-icons.png" alt="The text &quot;Happy Halloween&quot; and 3 app icons. The first depicts a piece of candy corn in front of a yellow chart. The second is a bowling pin with devil horns and a tail in front of a red chart. The third is a black witch's hat in front of a purple chart." /></p>

<figcaption>The text "Happy Halloween" and 3 app icons. The first depicts a piece of candy corn in front of a yellow chart. The second is a bowling pin with devil horns and a tail in front of a red chart. The third is a black witch's hat in front of a purple chart.</figcaption>

<h2 id="narrow">Narrow</h2>

<p>This season, instead of 5-Pin Bowling, as I usually do, I’ve opted to join a Lawn Bowling league. As a result and mostly as a joke, I’ve started a small Lawn Bowling app I’m calling Narrow. It’ll act partially as an opportunity to learn a bit of SwiftData in an environment where I can play around with it, and it will mostly simply record your ends and keep a record of your games.</p>

<p>So far it consists of just a short list of leagues, and you can create a date entry for a game, and add bowlers’ names to your game. Mostly I’ve been trying to workout how to get SwiftData to best interact with the Composable Architecture, as well as resisting just scrapping SwiftData altogether and returning to <a href="https://github.com/groue/GRDB.swift">GRDB</a>.</p>]]></content><author><name>Joseph Roque</name></author><category term="dev-log" /><category term="approach" /><category term="ios" /><category term="android" /><category term="swift" /><category term="programming" /><category term="bowling" /><summary type="html"><![CDATA[Recently I saw someone else’s dev log and I thought starting my own could be a good learning and reflection opportunity.]]></summary></entry><entry><title type="html">2024, Year of the Bird</title><link href="https://runcode.blog/2024/01/08/2024-year-of-the-bird.html" rel="alternate" type="text/html" title="2024, Year of the Bird" /><published>2024-01-08T21:00:00+00:00</published><updated>2024-01-08T21:00:00+00:00</updated><id>https://runcode.blog/2024/01/08/2024-year-of-the-bird</id><content type="html" xml:base="https://runcode.blog/2024/01/08/2024-year-of-the-bird.html"><![CDATA[<p>Each year I like to set some broad intentions I like to fallback on to help me shape the year. I didn’t really share this last year, but I had 2 broad themes for the year in my personal development: <a href="https://tryapproach.app/">Approach, my app for 5 Pin Bowling</a>, and the <a href="https://squamish50.com/">Squamish 50</a>.</p>

<p>For 2024, I’d like to set a new theme: Birds.</p>

<p>Birds have been among my favourite animals for years, and after getting more into photography with my first DSLR in 2018, I’ve wanted to focus my hobby. Loving nature so much, I tried to get out on hikes with my camera whenever possibly, and now I want to explore photography a little differently. So this year I’ll do that by trying to capture some birds!</p>

<p>As any hobby, I want this to be for me first. But I’m looking forward to sharing my photos, learning more about how to identify birds, and building a new personal birding journal.</p>

<p>See you out there 👋</p>]]></content><author><name>Joseph Roque</name></author><category term="personal" /><category term="birding" /><summary type="html"><![CDATA[Each year I like to set some broad intentions I like to fallback on to help me shape the year. I didn’t really share this last year, but I had 2 broad themes for the year in my personal development: Approach, my app for 5 Pin Bowling, and the Squamish 50.]]></summary></entry></feed>