Today, ScaleDiary 1.2 was released on the Apple App Store. ScaleDiary is the no-nonsense app that helps you track your weight every day, with clear graphs and charts to help you understand your progress over time. This release adds a feature I’ve wanted to build for a while, on top of a big rewrite of the app’s data layer that makes everything faster and more reliable. For more information see ScaleDiary.
Blog
Today, TimeTails was released on the Apple App Store. TimeTails is an app for your kids to learn to tell time. It’s a hands-on way of learning; kids can move the hands of the clock. The app is structured in the same way the clock is learned at school. It begins by learning the hour hand, then moves on to the minute hand and the different time intervals (30, 15, and 5 minutes). In addition to being hands-on, the theory and questions are also spoken out loud so that kids only need to know how to read the numbers. The Free Play mode also gives kids the opportunity to learn by experimenting. For more information see TimeTails. I plan to keep releasing updates to TimeTails in the coming weeks. Your feedback is therefore invaluable. Let me know what you think. You can download TimeTails here.
Today, I spent some time automating the deployment of this blog. This site is
generated using a static site generator called Hugo. While
I love Hugo, deployment is a bit different from other blogs than, for instance,
WordPress. In WordPress, you write and can directly publish the site. Using Hugo,
however, you write your posts and then the site is generated, much like a compiler
that generates machine code from the website you’ve written. Until now, this was a
very manual process. I had to write the blog post, test it locally, and copy the
post manually to my hosting provider. It wasn’t that much work to be honest, but
still it was a small hurdle when writing a post. It also prevented me from
automating some things, for instance, automatically creating a post when a new
version of an app was released. That changed today. I spent some time setting up
a Forgejo runner Docker container on my home server that automatically builds the site when
committed to the Git repository. When I deploy from the develop branch, the site
is deployed locally to that server, so I can directly observe how the new post
would look in production. When it’s then merged into the main branch, the site is deployed
here on thinkpractice.nl. Hopefully, automating this process will make it easier
for me to write and post. In any case, keep posted pun intended, because some
nice updates are coming soon!
I’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 #expect macro to
check whether the captured values were the correct ones. One such test looked like this:
@Test
func loginWithNoAccountsAndSuccessfulLoginSavesTokenInCredentialManager() async throws
{
let expectedCredentials = AccountCredentials(account: "test@instance.social",server: "instance.social", token: "mynewaccesstoken")
var instanceUrlCalled: URL?
var storedCredentials: AccountCredentials?
let appModel = withDependencies {
$0.defaultDatabase = try! appDatabase()
$0.socialMediaClient = MockSocialMediaServer(connectToInstance: { url in
instanceUrlCalled = url
return expectedCredentials
})
$0.accountCredentialsStorage.storeCredentials = { credentials in storedCredentials = credentials
}
} operation: {
AppModel()
}
#expect(appModel.destination == nil)
try await appModel.login()
guard let expectedUrl = URL(string: "https://instance.social") else {
Issue.record("Failed to creater URL from string")
return
}
#expect(appModel.destination.is(\.onboardingScreen))
#expect(expectedUrl == instanceUrlCalled)
#expect(expectedCredentials == storedCredentials)
}
However, capturing these variables caused problems in strict concurrency mode. I could lead to potential
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’t capture any values at all! The fix was as easy as putting the #expect macros in the closures passed to the mock, like this:
I love the new Swift Testing framework and have been using it whenever I can. However, I still have a lot of legacy code that uses XCTest and I want to convert it to the new framework. This includes a lot of grunt work that I was thinking I hopefully could automate with GitHub Copilot. However, when I tried to do this, I found that Copilot often would create a mingle of XCTest and Swift Testing code, or stubbornly stuck to XCTest even when I asked it to use Swift Testing.
In the previous post, we completed a first version of our app, which can spice 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, you might have noticed that the app is not perfect. It can sometimes misidentify animals, and it can also fail to 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 “canine” or even a “mammal”. Why does this happen? We will explore the answer to this question in this post.
Over the course of the past few posts (see the overview here), we’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.
Creating a Protocol to Define Image Classifiers #
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:
In the previous post, we implemented the initial screen and the ImagePicker view. In this post, we will further develop the app. We will create a ViewModel and a ScannedAnimal model, and add the ‘Main’ and ‘Detail’ 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’re looking for an overview of all the posts in this series, you can find them here.
In the previous post, I introduced the ZooScan app idea and shared a demo of the app in action. In this post, we’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’re looking for an overview of all the posts in this series, you can find them here.
To give you a basic idea of what we’ll be doing, here is an animated GIF that shows the app in action.
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 SeaScan 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?
Showing 1 to 10 of 17 results