<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>App Development on ThinkPractice: Smart Solutions to Practical Problems</title>
    <link>https://thinkpractice.nl/tags/app-development/</link>
    <description>Recent content in App Development on ThinkPractice: Smart Solutions to Practical Problems</description>
    <generator>Hugo</generator>
    <language>en-us</language>
    <lastBuildDate>Tue, 09 Sep 2025 13:21:13 +0200</lastBuildDate>
    <atom:link href="https://thinkpractice.nl/tags/app-development/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Capturing values in Swift Testing in strict concurrency mode</title>
      <link>https://thinkpractice.nl/post/capturing_values_in_swift_testing/</link>
      <pubDate>Tue, 09 Sep 2025 13:21:13 +0200</pubDate>
      <guid>https://thinkpractice.nl/post/capturing_values_in_swift_testing/</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve recently turned on Swift 6.0 mode on of my projects and that means migrating all code to strict concurrency. I had some tests that used mocks to capture values and then using an &lt;code&gt;#expect&lt;/code&gt; macro to&#xA;check whether the captured values were the correct ones. One such test looked like this:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-swift&#34; data-lang=&#34;swift&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@Test&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;loginWithNoAccountsAndSuccessfulLoginSavesTokenInCredentialManager&lt;/span&gt;() async &lt;span style=&#34;color:#ff79c6&#34;&gt;throws&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;expectedCredentials&lt;/span&gt; = AccountCredentials(account: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;test@instance.social&amp;#34;&lt;/span&gt;,server: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;instance.social&amp;#34;&lt;/span&gt;, token: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;mynewaccesstoken&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;instanceUrlCalled&lt;/span&gt;: URL?&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;storedCredentials&lt;/span&gt;: AccountCredentials?&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;appModel&lt;/span&gt; = withDependencies {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;$0&lt;/span&gt;.defaultDatabase = &lt;span style=&#34;color:#ff79c6&#34;&gt;try&lt;/span&gt;! appDatabase()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;$0&lt;/span&gt;.socialMediaClient = MockSocialMediaServer(connectToInstance:  { url &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            instanceUrlCalled = url&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; expectedCredentials&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        })&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;$0&lt;/span&gt;.accountCredentialsStorage.storeCredentials = { credentials &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; storedCredentials = credentials&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    } operation: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        AppModel()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    #expect(appModel.destination == &lt;span style=&#34;color:#ff79c6&#34;&gt;nil&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;try&lt;/span&gt; await appModel.login()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;guard&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;expectedUrl&lt;/span&gt; = URL(string: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;https://instance.social&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;else&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Issue.record(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Failed to creater URL from string&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    #expect(appModel.destination.&lt;span style=&#34;color:#ff79c6&#34;&gt;is&lt;/span&gt;(\.onboardingScreen))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    #expect(expectedUrl == instanceUrlCalled)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    #expect(expectedCredentials == storedCredentials)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;However, capturing these variables caused problems in strict concurrency mode. I could lead to potential&#xA;data races and therefore swift 6.0 does not allow it. It took me some time to figure out how to fix this, and it turned out you shouldn&amp;rsquo;t capture any values at all! The fix was as easy as putting the &lt;code&gt;#expect&lt;/code&gt; macros in the closures passed to the mock, like this:&lt;/p&gt;</description>
    </item>
    <item>
      <title>ZooScan - Part 5: Multi-Label vs. Multi-Class Classification</title>
      <link>https://thinkpractice.nl/post/zooscan_5/</link>
      <pubDate>Thu, 12 Jun 2025 09:58:09 +0200</pubDate>
      <guid>https://thinkpractice.nl/post/zooscan_5/</guid>
      <description>&lt;p&gt;In the previous &lt;a href=&#34;https://thinkpractice.nl/post/zooscan_4/&#34;&gt;post&lt;/a&gt;, we completed a first version of our app, which can spice&#xA;up your visits to a zoo by identifying the animals you see. If, like me, you took it for a spin to your local zoo,&#xA;you might have noticed that the app is not perfect. It can sometimes misidentify animals, and it can also fail to&#xA;assign a label at the correct level of specificity. For example, it might identify a zebra as a horse, or it might not recognize a Labrador as a &amp;ldquo;canine&amp;rdquo; or even a &amp;ldquo;mammal&amp;rdquo;. Why does this happen? We will explore the answer to this question in this post.&lt;/p&gt;</description>
    </item>
    <item>
      <title>ZooScan - Part 4: Using the Swift Vision Framework to Classify Animals</title>
      <link>https://thinkpractice.nl/post/zooscan_4/</link>
      <pubDate>Wed, 11 Jun 2025 10:22:01 +0200</pubDate>
      <guid>https://thinkpractice.nl/post/zooscan_4/</guid>
      <description>&lt;p&gt;Over the course of the past few posts (see the overview &lt;a href=&#34;https://thinkpractice.nl/courses/#zooscan-tutorial&#34;&gt;here&lt;/a&gt;), we&amp;rsquo;ve introduced the ZooScan app and developed its UI using SwiftUI. In this fourth part, we will focus on integrating the Swift Vision framework to classify animals based on images captured by the app.&lt;/p&gt;&#xA;&lt;h2 id=&#34;creating-a-protocol-to-define-image-classifiers&#34;&gt;&#xA;  Creating a Protocol to Define Image Classifiers&#xA;  &lt;a class=&#34;anchor-link&#34; href=&#34;#creating-a-protocol-to-define-image-classifiers&#34; style=&#34;text-decoration: none !important;&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&lt;p&gt;The first step is defining a protocol for our animal classification model. By using a standardized interface, we can easily switch between different models in the future if needed. Here’s how we can define the protocol:&lt;/p&gt;</description>
    </item>
    <item>
      <title>ZooScan – Part 3: Storing Our Scanned Animals and Finalizing the UI</title>
      <link>https://thinkpractice.nl/post/zooscan_3/</link>
      <pubDate>Sat, 07 Jun 2025 10:15:50 +0200</pubDate>
      <guid>https://thinkpractice.nl/post/zooscan_3/</guid>
      <description>&lt;p&gt;In the &lt;a href=&#34;https://thinkpractice.nl/post/zooscan_2/&#34;&gt;previous post&lt;/a&gt;, we implemented the initial screen and the &lt;code&gt;ImagePicker&lt;/code&gt; view. In this post, we will further develop the app. We will create a &lt;code&gt;ViewModel&lt;/code&gt; and a &lt;code&gt;ScannedAnimal&lt;/code&gt; model, and add the &amp;lsquo;Main&amp;rsquo; and &amp;lsquo;Detail&amp;rsquo; views.  This will allow us to focus on the UI and the app structure before we dive into the machine learning part in later posts.  By the way, if you&amp;rsquo;re looking for an overview of all the posts in this series, you can find them &lt;a href=&#34;https://thinkpractice.nl/courses/#zooscan-tutorial&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>ZooScan - Part 2: Project Setup and the First Steps</title>
      <link>https://thinkpractice.nl/post/zooscan_2/</link>
      <pubDate>Wed, 04 Jun 2025 16:34:47 +0200</pubDate>
      <guid>https://thinkpractice.nl/post/zooscan_2/</guid>
      <description>&lt;p&gt;In the &lt;a href=&#34;https://thinkpractice.nl/post/zooscan/&#34;&gt;previous post&lt;/a&gt;, I introduced the ZooScan app idea and shared a demo of the app in action. In this post, we&amp;rsquo;ll be getting our hands dirty. We will set up the project, create the basic UI, and implement the first steps of the app. By the way, if you&amp;rsquo;re looking for an overview of all the posts in this series, you can find them &lt;a href=&#34;https://thinkpractice.nl/courses/#zooscan-tutorial&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;To give you a basic idea of what we&amp;rsquo;ll be doing, here is an animated GIF that shows the app in action.&lt;/p&gt;</description>
    </item>
    <item>
      <title>ZooScan - Part 1: Building Zooscan - An App that Scans and Classifies Zoo Animals</title>
      <link>https://thinkpractice.nl/post/zooscan/</link>
      <pubDate>Tue, 03 Jun 2025 12:14:16 +0200</pubDate>
      <guid>https://thinkpractice.nl/post/zooscan/</guid>
      <description>&lt;p&gt;My son has always been fascinated by animals.  We go to the local zoo multiple times a week, and when we’re on holiday, we always make a point to visit local zoos and other animal parks. On one of our holidays in Porto, we visited the local SeaLife. While we were there, their &lt;a href=&#34;https://apps.apple.com/us/app/seascan/id1643493357&#34;&gt;SeaScan&lt;/a&gt; app caught my attention. his clever app lets you scan fish and other creatures in the aquarium to instantly learn more about them. That sparked an idea: what if I build a similar app for zoo animals?&lt;/p&gt;</description>
    </item>
    <item>
      <title>Five Promising AI areas for App Developers</title>
      <link>https://thinkpractice.nl/post/smart_apps/</link>
      <pubDate>Mon, 26 May 2025 10:11:07 +0200</pubDate>
      <guid>https://thinkpractice.nl/post/smart_apps/</guid>
      <description>&lt;p&gt;Over the past few years, we&amp;rsquo;ve seen some truly impressive advancements in AI and machine learning. I remember that 20 years ago, when I was studying Artificial Intelligence, even finding faces in images was a major challenge—something that smartphones now do effortlessly in real time. The same goes for speech recognition: once considered nearly impossible, it&amp;rsquo;s now embedded in tools like Siri, Alexa, Google Assistant, and even real-time translation apps. While voice assistants are still not perfect—as this &lt;a href=&#34;https://mastodon.world/@jeffowski/113926166177811970&#34;&gt;video&lt;/a&gt; humorously shows—it&amp;rsquo;s clear that AI has come a long way. Today, AI opens the door to exciting new applications that were unimaginable just a few years ago. In this post, we explore key areas of AI and ML that app developers can leverage to create smarter, more engaging experiences. First, let&amp;rsquo;s look at Image Recognition&amp;hellip;&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
