
Ole Begemann's Blog
1,413 FOLLOWERS
Ole is an iOS and Mac developer from Berlin. He's written about software development on Apple platforms since 2009. Though he publishes only a few articles a year, all of them are worthwhile reading. You can subscribe to get notified once he updates a new one.
Ole Begemann's Blog
2M ago
And what it can teach us about SwiftUI’s stack layout algorithm
I have one more thing to say on the relative sizing view modifier from my previous post, Working with percentages in SwiftUI layout. I’m assuming you’ve read that article. The following is good to know if you want to use the modifier in your own code, but I hope you’ll also learn some general tidbits about SwiftUI’s layout algorithm for HStacks and VStacks.
Using relative sizing inside a stack view
Let’s apply the relativeProposed modifier to one of the subviews of an HStack:
HStack(spacing: 10) {
Color.blue
.relativ ..read more
Ole Begemann's Blog
2M ago
SwiftUI’s layout primitives generally don’t provide relative sizing options, e.g. “make this view 50 % of the width of its container”. Let’s build our own!
Use case: chat bubbles
Consider this chat conversation view as an example of what I want to build. The chat bubbles always remain 80 % as wide as their container as the view is resized:
The chat bubbles should become 80 % as wide as their container. Download video
Building a proportional sizing modifier 1. The Layout
We can build our own relative sizing modifier on top of the Layout protocol. The layout multiplies its own proposed size (whi ..read more
Ole Begemann's Blog
2M ago
Problem
The Photos app on macOS doesn’t provide a keyboard shortcut for the Export Unmodified Original command.
macOS allows you to add your own app-specific keyboard shortcuts via System Settings > Keyboard > Keyboard Shortcuts > App Shortcuts. You need to enter the exact spelling of the menu item you want to invoke.
Photos renames the command depending on what’s selected: Export Unmodified Original For 1 Photo“ turns into ”… Originals For 2 Videos” turns into “… For 3 Items” (for mixed selections), and so on. Argh!
The System Settings UI for assigning keyboard shortcuts is extremel ..read more
Ole Begemann's Blog
3M ago
I rarely participate actively in the Swift Evolution process, but I frequently refer to evolution proposals for my work, often multiple times per week. The proposals aren’t always easy to read, but they’re the most comprehensive (and sometimes only) documentation we have for many Swift features.
For years, my tool of choice for searching Swift Evolution proposals has been Karoy Lorentey’s swift-evolution workflow for Alfred.
The workflow broke recently due to data format changes. Karoy was kind enough to add me as a maintainer so I could fix it.
The new version 2.1.0 is now available on GitHu ..read more
Ole Begemann's Blog
3M ago
tl;dr Foundation overloads the pattern matching operator ~= to enable matching against error codes in catch clauses.
catch clauses in Swift support pattern matching, using the same patterns you’d use in a case clause inside a switch or in an if case … statement. For example, to handle a file-not-found error you might write:
import Foundation
do {
let fileURL = URL(filePath: "/abc") // non-existent file
let data = try Data(contentsOf: fileURL)
} catch let error as CocoaError where error.code == .fileReadNoSuchFile {
print("File doesn't exist")
} catch {
print("Other error ..read more
Ole Begemann's Blog
4M ago
I know I’m almost a decade late to this party, but I’m probably not the only one, so here goes.
Double Fine Adventure was a wildly successful 2012 Kickstarter project to crowdfund the development of a point-and-click adventure game and, crucially, to document its development on video. The resulting game Broken Age was eventually released in two parts in 2014 and 2015. Broken Age is a beautiful game and I recommend you try it. It’s available for lots of platforms and is pretty cheap (10–15 euros/dollars or less). I played it on the Nintendo Switch, which worked very well.
Broken Age.
But the r ..read more
Ole Begemann's Blog
6M ago
The clipped() modifier in SwiftUI clips a view to its bounds, hiding any out-of-bounds content. But note that clipping doesn’t affect hit testing; the clipped view can still receive taps/clicks outside the visible area.
I tested this on iOS 16.1 and macOS 13.0.
Example
Here’s a 300×300 square, which we then constrain to a 100×100 frame. I also added a border around the outer frame to visualize the views:
Rectangle()
.fill(.orange.gradient)
.frame(width: 300, height: 300)
// Set view to 100×100 → renders out of bounds
.frame(width: 100, height: 100)
.border(.blue)
SwiftUI views don ..read more
Ole Begemann's Blog
7M ago
On the positioning of the .animation modifier in the view tree, or: “Rendering” vs. “non-rendering” view modifiers
The documentation for SwiftUI’s animation modifier says:
Applies the given animation to this view when the specified value changes.
This sounds unambiguous to me: it sets the animation for “this view”, i.e. the part of the view tree that .animation is being applied to. This should give us complete control over which modifiers we want to animate, right? Unfortunately, it’s not that simple: it’s easy to run into situations where a view change inside an animated subtree doesn’t get ..read more
Ole Begemann's Blog
8M ago
Mac apps built with Xcode 14.0 and 14.0.1 may contain concurrency bugs because the Swift 5.7 compiler can generate invalid code when targeting the macOS 12.3 SDK. If you distribute Mac apps, you should build them with Xcode 13.4.1 until Xcode 14.1 is released.
Here’s what happened:
Swift 5.7 implements SE-0338: Clarify the Execution of Non-Actor-Isolated Async Functions, which introduces new rules how async functions hop between executors. Because of SE-0338, when compiling concurrency code, the Swift 5.7 compiler places executor hops in different places than Swift 5.6.
Some standard libra ..read more
Ole Begemann's Blog
8M ago
SwiftUI’s .task modifier inherits its actor context from the surrounding function. If you call .task inside a view’s body property, the async operation will run on the main actor because View.body is (semi-secretly) annotated with @MainActor. However, if you call .task from a helper property or function that isn’t @MainActor-annotated, the async operation will run in the cooperative thread pool.
Example
Here’s an example. Notice the two .task modifiers in body and helperView. The code is identical in both, yet only one of them compiles — in helperView, the call to a main-actor-isolated functio ..read more