Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Meeting minutes and learnings from the physical space meeting.

aflockofswifts/meetings

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

A Flock of Swifts

Flock We are a group of people excited by the Swift language. We meet each Saturday morning to share and discuss Swift-related topics.

All people and all skill levels are welcome to join.
RSVP: https://www.meetup.com/A-Flock-of-Swifts/

Archives


2025.10.18

Server‑Side Swift / Middleware

Conferences & Communities

Symbols & Vector Design Tools

Alex created a script to produce custom SF symbols. He is working on possibly open sourcing it.

Vector drawing: Figma, Sketch, Affinity Designer, Inkscape

Articles & Reading

AI Model Sizes & Local Models

  • Bob DeLaurentis: (from web search) “Claude 3.5 Sonnet ~175B params; Claude 4 ~1.7T (unverified).”
  • Josh Homann: “I run the 20B GPT‑OSS on ollama.”
  • Mihaela MJ: OpenAI GPT‑OSS intro
    https://openai.com/index/introducing-gpt-oss/

2025.10.11

Swift & Apple APIs

Swift Packages & Libraries

UUID Extras Package

ViewModels are just a Function

Foundation Models

.task {
    do {
        let instructions =
        """
        You are a screen writer pitching ideas for a screenplay.
        Your response should be a synopsis of the plot followed by a bulleted list
        of major characters with a brief biography.
        """
        let options = GenerationOptions(sampling: .greedy)
        let session = LanguageModelSession(instructions: instructions)
        session.prewarm()
        let prompt = "Write a murder mystery set on a train"
        let response = try await session.respond(to: prompt,
                           generating: ScreenPlaySynopsis.self, options: options)
        print(response)
    } catch {
        print(error)
    }
}

2025.10.04

Algorithms & Interview Prep

Global Actor Example

@globalActor
actor MyActor {
    static let shared = MyActor()
    private init() { }
}

AI & Tools

Using Foundation Models

An example from Josh Homann.

import FoundationModels
let instructions =
"""
    All recipes should be vegan. Start every answer with a bulleted list of ingredients followed by a description of the dish followed by a numbered list of instructions.
"""
let options = GenerationOptions(temperature: 5.0)
let session = LanguageModelSession(instructions: instructions)

let prompt = "create a recipe for Mapo Tofu"
let response = try await session.respond(to: prompt, options: options)
print(response.content)

2025.09.27

AI Generated History

Allen used Claude to go through all of the pages documents that had a high level UML-like diagram of his design turn them into a movie.

Gestures

There are things that you still cannot do with pure SwiftUI.

UI Design

Be careful about using SF symbol as images because they lose their original type-based semantics.

Consider using scaled metric: https://developer.apple.com/documentation/swiftui/scaledmetric

Liquid Glass

    Text("Hello, World!")
        .font(.title)
        .padding()
        .glassEffect(.regular.tint(.orange).interactive())

App Intents

Interesting impromptu discussion with Joe leading about how he uses app intents in some of his apps. Here are some resources:

Trip Advisor Moving to from MVVM-C to TCA

Shared by Rainer.

https://medium.com/tripadvisor/the-evolution-of-native-engineering-at-tripadvisor-part-1-577cc0e36ec8

Article Roundup from Peter

In depth coverage of how SwiftUI view updates.

Also from Josh:

Async algorithm share:

https://github.com/apple/swift-async-algorithms/blob/main/Evolution/0016-share.md

Article Roundup from Josh

A bunch of shortcuts to produce things like screenshots that you can include in git commits.

An IDE for Shortcuts on macOS.

It is suprising just how slow NSImage is. If you are on the Mac, make sure you are using CGImage.

If you use the command line, this is how you can create code completion for zsh.

This is a deep dive on how rendering in iOS works.

Matt Mssicotte's take on when to use actors. (Which Josh doesn't seem to agree with but found it to be an interesting post.)


2025.09.20

To OpenAI or Not

Carlyn talked about when it makes sense to generate your APIs using OpenAPI.

Some other options (resources):

Talking Architecture

Recreating Stateobject: https://fatbobman.com/en/posts/lazy-initialization-state-in-swiftui/

Architecture

Memory Safety

Ray F. did an exploration of the new Swift 6.2 strict memory enforcement.

We started by enabling checking in the package.

// swift-tools-version: 6.2

And in the target:

.target(name: "uuid-exploration"),
        swiftSettings: [.strictMemorySafety(), .treatAllWarnings(as: .error)])

You can enable from the command line or as an argument in your Xcode project's build:

  -strict-memory-safety

Then we fixed the warnings (errors) by adding unsafe to every place that required it.

unsafe withUnsafeMutableBytes(of: &random) { raw in
    let result = unsafe SecRandomCopyBytes(kSecRandomDefault, 10, raw.baseAddress!)
    precondition(result == errSecSuccess)
}

Exploring a Design System

Going back to the homework from Josh.

HTML/CSS are very good about separating semantics and style. SwiftUI, similarly can be decomposed into different parts.

As example consider this view which is all about content:

struct ContentView: View {
    var body: some View {
        List {
            ForEach(0..<10) { i in
                LabeledContent {
                    Text("Title")
                        .font(.body)
                        .foregroundStyle(.red)
                    Text("Subtitle")
                    Text("Subsubtitle")
                    Text("Subsubsubtitle")
                } label: {
                    Label("hello", systemImage: "star.fill")
                }
                .labeledContentStyle(.verticalHierarchicalText)
            }
        }
    }
}

The style is applied with a custom modifier and can be defined like so:

struct HierarchicalLabeledContentStyle: LabeledContentStyle {
    var orientation: Axis = .horizontal
    var spacing: Double = .zero
    enum Constant {
        static let fontFromIndex: [Int: Font] = [ 1 : .body, 2: .caption2, 3 : .caption]
        static let shapeStyleFromIndex: [Int: AnyShapeStyle] = [ 1 : AnyShapeStyle(.primary), 2: nyShapeStyle(.secondary), 3 : AnyShapeStyle(.tertiary)]
    }

    func makeBody(configuration: Configuration) -> some View {
        let layout = switch orientation {
          case .horizontal: AnyLayout(HStackLayout(spacing: spacing))
          case .vertical: AnyLayout(VStackLayout(alignment: .leading, spacing: spacing))
        }
        HStack(spacing: 10) {
          configuration.label
                .alignmentGuide(.listRowSeparatorLeading) { $0[.trailing] + 10 }
  
            layout {
                var index = 0
                ForEach(subviews: configuration.content) { subview in
                    subview.font(Constant.fontFromIndex[index] ?? .footnote)
                        .foregroundStyle(Constant.shapeStyleFromIndex[index] ?? AnyShapeStyle(.quaternary))
                    let _ = index += 1
                }
            }
        }
    }
}

To allow for ergonomic auto-completing modifiers:

extension LabeledContentStyle where Self == HierarchicalLabeledContentStyle {
    static var horizontal: Self {
        Self(orientation: .horizontal)
    }
    static var verticalHierarchicalText: Self {
        Self(orientation: .vertical)
    }
    static func verticalHierarchicalText(spacing: Double) -> Self {
        Self(orientation: .vertical, spacing: spacing)
    }
}

2025.09.13

Apple Security & Updates

Projects & Apps

Links & References

Swift & Interpreter

Containers & Servers

Carlyn led a presentation on containers.

Other container references

--

2025.09.06

Retro-computing

John Brewer informed us that Microsoft released the source code BASIC for the 6502.

It is about 8000 lines and compiles for multiple platforms, including the Apple II, Commodore PET and others.

UUID

Universal Unique IDs are an important part of application development are defined by RFC 4122 and is being updated in RFC 9562. See https://www.rfc-editor.org/rfc/rfc9562.html

The Foundation implementation is version 4 of RFC 4122 and contains 120 bits of randomness. Ray walked us through an extension that implements version 7 which uses Unix-based time so that UUIDs can be sorted in order.

The first thing we did was make a setter from a raw UInt128. The first implementation looks like this:

extension UUID {
  @inlinable init(_ value: UInt128) {
    let v: uuid_t =
      (UInt8(value >> 120 & 0xff),
       UInt8(value >> 112 & 0xff),
       UInt8(value >> 104 & 0xff),
       UInt8(value >> 96 & 0xff),
       UInt8(value >> 88 & 0xff),
       UInt8(value >> 80 & 0xff),
       UInt8(value >> 72 & 0xff),
       UInt8(value >> 64 & 0xff),
       UInt8(value >> 56 & 0xff),
       UInt8(value >> 48 & 0xff),
       UInt8(value >> 40 & 0xff),
       UInt8(value >> 32 & 0xff),
       UInt8(value >> 24 & 0xff),
       UInt8(value >> 16 & 0xff),
       UInt8(value >> 8 & 0xff),
       UInt8(value & 0xff))
    self = UUID(uuid: v)
  }
}

Turns out that this is only valid for little endian platforms (which is most everything these days) but might fail on an embedded platform. Here is the final version:

import Foundation
    
extension UUID {
      
  var version: Int {
    Int((uuid.6 & 0xf0) >> 4)
  }
      
  enum Variant {
     case ncs, rfc9562, guid, future
   }
       
   var variant: Variant {
       switch (uuid.8 >> 4) & 0xf {
       case 0x0 ... 0x7: .ncs
       case 0x8 ... 0xb: .rfc9562
       case 0xc ... 0xd: .guid
       case 0xe ... 0xf: .future
       }
   }
      
  init(_ value: UInt128) {
    var bigEndian = value.bigEndian
    let t = withUnsafeBytes(of: &bigEndian) { raw -> uuid_t in
      raw.load(as: uuid_t.self)
    }
    self = UUID(uuid: t)
  }
      
  var value: UInt128 {
    var source = uuid
    var destination: UInt128 = 0        
    _ = withUnsafeBytes(of: &source) { src in
      withUnsafeMutableBytes(of: &destination) { dst in
        src.copyBytes(to: dst)
      }
    }        
    return Int(littleEndian: 42) == 42 ? destination.byteSwapped : destination
  }

  static func v7() -> UUID {
      let timestamp = UInt128(Date().timeIntervalSince1970 * 1000)
      var random = UInt128.zero
      withUnsafeMutableBytes(of: &random) { raw in
        let result = SecRandomCopyBytes(kSecRandomDefault, 10, raw.baseAddress!)
        assert(result == 0)
      }
        
      var value = UInt128(0x7000_0000_000000000000)
      value |= (timestamp & ((UInt128(1) << 48) - 1)) << 80
      value |= (random & ((UInt128(1) << 76) - 1))
        
      // Patch in the variant
      value &= ~(UInt128(0b11) << 62) // clear the two bits
      value |=  (UInt128(0b10) << 62) // set to 10
        
      return UUID(value)
    }
      
  var unixMilliseconds: UInt64? {
     guard version == 7 else { return nil }
     return UInt64((value >> 80) & ((UInt128(1) << 48) - 1))
   }
      
  var timestamp: Date? {
    guard let unixMilliseconds else { return nil }
    return Date(timeIntervalSince1970: TimeInterval(unixMilliseconds) / 1000.0)
  }
}

We also wrote some tests:

import Testing
import Foundation
@testable import uuid_exploration
    
@Suite struct UUIDTests {
  @Test func version() {
    let uuid = UUID()
    #expect(uuid.version == 4)
    #expect(uuid.timestamp == nil)
  }
      
  @Test func initialize() {
    let x = UUID(1)
    #expect(x.uuidString == "00000000-0000-0000-0000-000000000001")
    #expect(x.value == 1)
        
    let y = UUID(0x112233445566778899AABBCCDDEEFF00)
    #expect(y.uuidString == "11223344-5566-7788-99AA-BBCCDDEEFF00")
    #expect(y.value == 0x112233445566778899AABBCCDDEEFF00)
  }
      
  @Test func generateUUID7() {
    let uuid = UUID.v7()
    #expect(uuid.version == 7)
    print(uuid.uuidString)
    print(uuid.timestamp!)
  }
}

We also tested with this web service:

Making a Random Label

From Josh:

print(String(Int.random(in: 0..<Int.max), radix: 36).uppercased())

Alternatively:

print(ObjectIdentifier(a).debugDescription)

Homework

From Josh. Read up on this for next week:


2025.08.30

Numerics Release 1.1

This release introduces relaxed addition (that can use FMA instructions) for an order of magnitude performance gain.

https://github.com/apple/swift-numerics/releases/tag/1.1.0

Issues & Debugging

  • Joe: With iOS 26 I get Xcode console messages for “Attribute Graph: cycle detected through attribute” when using the new tab bar bottom accessory. Anyone else seeing these?

Sendable, Sending, and Nonsending

Global Actor Example

  • Josh Homann: Example of global actor
@globalActor
actor MyActor {
    static let shared = MyActor()
    private init() { }
}

Containers

  • carlyn: I’ve started using containers! Will say more next week when I’m more confident.

https://www.whynotestflight.com/excuses/podman-how-do-i-get-and-edit-an-image-on-macos/

Subscriptions & Assign

  • Josh Homann: Subscriptions class
nonisolated final class Subscriptions {
    private var disposables: [() -> Void] = []
    deinit { disposables.forEach { $0() } }
    static func += <Value> (lhs: Subscriptions, rhs: Task<Value, some Error>) {
        lhs.disposables.append(rhs.cancel)
    }
}
  • Josh Homann: Generic assign function
func assign<each Value, Target: AnyObject>(
        on target: Target,
        to keyPaths: repeat ReferenceWritableKeyPath<Target, each Value>
    ) -> Task<Void, Never> where Element == (repeat each Value) {
        subscribe(referencing: target) { target, values in
            for (value, keyPath) in repeat (each values, each keyPaths) {
                target[keyPath: keyPath] = value
            }
        }
    }

Text Rendering

  • Josh Homann: Custom Renderer for text blur effect
struct Renderer: TextRenderer {
    var time: TimeInterval
    func draw(layout: Text.Layout, in context: inout GraphicsContext) {
        let glyphs = layout.lazy.flatMap(\.self).flatMap(\.self)
        let currentTime = sin(time)
        for (offset, glyph) in glyphs.enumerated() {
            var context = context
            let blur = abs(Double(offset) * currentTime)
            context.addFilter(.blur(radius: blur))
            context.translateBy(x: 0, y: Double(offset) * 5 * currentTime)
            context.draw(glyph)
        }
    }
}
  • Josh Homann: Example ContentView using TimelineView
struct ContentView: View {
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundStyle(.tint)
            TimelineView(.animation) { context in
                Text("Hello, world!")
                    .font(.largeTitle)
                    .textRenderer(Renderer(time: context.date.timeIntervalSince1970))
            }
        }
        .padding()
    }
}

2025.08.23

Payments

It is wise to not do encryption and payment systems yourself.

Authentication

Passwords & Security Tools

ARKit

Swift Observation Extensions

Using observed to replace combine.

  • Josh Homann: Shared Observation.Observable extension snippet:
extension Observation.Observable where Self: AnyObject {
    func values<Value: Sendable>(of keyPath: KeyPath<Self, Value>) -> some AsyncSequence<Value, Never> {
        Observations.untilFinished { [weak self] in
            self.map { .next($0[keyPath: keyPath]) } ?? .finish
        }
    }
    func newValues<Value: Sendable>(of keyPath: KeyPath<Self, Value>) -> some AsyncSequence<Value, Never> {
        values(of: keyPath).dropFirst()
    }
}
  • Josh Homann: Example @Observable Person class:
@Observable
@MainActor
final class Person: Sendable {
    var name = "Joshua"
    var age = 25
    init() {
        let a = newValues(of: \.name)
        Task {
            try await Task.sleep(for: .seconds(1))
            name = "Mark"
        }
        Task {
            for await name in a {
                print(name)
            }
        }
    }
}

2025.08.16

Hummingbird Adventures from Carlyn

If you need a static site generator:

https://github.com/twostraws/Ignite

JavaScript in Swift

A live example:

import JavaScriptCore

let context = JSContext()
let squareScript = """
var a = 1;
function square(number) {
    return number * number;
}
"""
context?.evaluateScript(squareScript)
let square = context?.objectForKeyedSubscript("square")
print(square?.call(withArguments: [2]).toDouble())

Protobuf & WWDC

App Store Guidelines & Hybrid Apps

References & Utilities

  • Josh Homann: SwiftUI TabView for page controller snippet:
TabView {
    // ... views ...
}
.tabViewStyle(.page(indexDisplayMode: .always))

Swift Evolution


2025.08.09

AI Chat

Speaking of AI

Interface talk

Bartosz Milewski Category Theory

Cool Stuff

Memory & System APIs

  • Frank Lefebvre: About available memory: on iOS (and all other platforms except macOS) you can use os_proc_available_memory()

Async & Extensions

Assembly Discussion

Swift Evolution — @Observable

https://github.com/swiftlang/swift-evolution/blob/main/proposals/0475-observed.md

import Swift
import Observation

@Observable
final class Person: Sendable {
    var name = "John"
    var age = 30
    init(name: String = "John", age: Int = 30) {
        self.name = name
        self.age = age
    }
}

extension Observation.Observable where Self: Sendable & AnyObject {
    func changes<Value: Sendable>(in transform: @escaping @Sendable (Self) -> Value) -> some AsyncSequence<Value, Never> {
        Observations.untilFinished { [weak self] in
            if let self {
                return .next(transform(self))
            } else {
                return .finish
            }
        }
    }
}

let p = Person()
let names = p.changes { $0.name }

Task {
    for await name in names {
        print(name)
    }
}

Task {
    try await Task.sleep(for: .seconds(1))
    p.name = "Jane"
    try await Task.sleep(nanoseconds: 0)
    p.name = "Jim"
}

2025.08.05

Swift Async Algorithms

Sharing async streams begins a new round of development.

Observation & Perception

Swift Evolution & Diffing

Keyframe Animations

From Alex:

KeyframeTrack(\.x) {
    MoveKeyframe(0)
    LinearKeyframe(0, duration: appearDuration)
    let keyframes = spiralKeyframes(cos, targetAngle: a, targetRadius: r, duration: flyDuration)
    KeyframeTrackContentBuilder.buildArray(keyframes)
}

Comment:

"This line allows me to use runtime-generated array of keyframes inside track content builder: KeyframeTrackContentBuilder.buildArray()"

SwiftUI & UI Tutorials

Open Source Projects

How Default Actor Isolation introduces problems and how to fix them


2025.07.26

Swift Composable Architecture (TCA)

Community & Chat Groups

Swift Language & Features

Swift & C++ Interop

Swift Embedded / Pico Projects

MCP (Model Context Protocol)

Swift Concurrency

Mesh Gradients in SwiftUI

struct MeshGradientView: View {
    @State var isAnimating = false
    
    var body: some View {
        MeshGradient(width: 3, height: 3, points: [
            [0.0, 0.0], [0.5, 0.0], [1.0, 0.0],
            [0.0, 0.5], [isAnimating ? 0.1 : 0.8, 0.5], [1.0, isAnimating ? 0.5 : 1],
            [0.0, 1.0], [0.5, 1.0], [1.0, 1.0]
        ], colors: [
            .purple, .indigo, .purple,
            isAnimating ? .mint : .purple, .blue, .blue,
            .purple, .indigo, .purple
        ])
        .edgesIgnoringSafeArea(.all)
        .onAppear() {
            withAnimation(.easeInOut(duration: 3.0).repeatForever(autoreverses: true)) {
                isAnimating.toggle()
            }
        }
    }
}

UIKit & Transparent Tab Bars

From Joe:

Found what I needed to get my UIKit (storyboard and UIViewControllerRepresentables) to work with the transparent tab bar in iOS 18 and iOS 26 
    
    .ignoresSafeArea(.all, edges: .bottom)
    .toolbarBackgroundVisibility(.hidden, for: .tabBar)

AI Agent IDE & ChatGPT Commentary

Misc Tools


2025.07.12

CMake Examples

Waiting for Observations

The new Observations API is still not available in the current Xcode beta 3.

Emoji Game

Josh talked about a solution for the new apple emoji game. The correspondance problem between emoji images and word solutions probably requires an LLM.

You can solve the word problem independently. An old game that Josh used a trie to solve the Boggle game.

A more generalized type of trie useful for the emoji game is a "compressed suffix trie" which can quickly match substrings.

Cancelling TaskGroup

Peter investigating the subtleties of TaskGroup and cancellation.

“Since a TaskGroup is a structured concurrency primitive, cancellation is automatically propagated through all of its child-tasks (and their child tasks).”

Solutions for Different Layout

These blog posts go into quite a bit of depth about how things work.

Another solution is to create a variant of Text that never truncates and use it in combination with ViewThatFits.

AI and XcodeBuild

From Chitaranjan:


2025.07.05

Embedded Swift

Frank Lefebvre is teaching a workshop on embedded Swift. Here is a link to some of the materials he has used.

Carlyn suggests starting with something simpler than Swift. (i.e. Vendor environments tends to be highly streamlined.)

Also, for board design:

A cool clock design with 48 stepper motors. Alex is experimenting building something like it and showed:

Another motor based art project from Carlyn

Question about customizing the output of DoCC

Here are some general resources:

(Sounds like Carlyn is hoping to inspect the AST and build a custom DOCC backend. The most relevant one supplied by Bob.)

Carlyn's docc notes:

Puzzles

YYUR, YYUB, ICUR YY4ME

There is a new puzzle game that Apple released in the news App. It requires you to drag emoji combination.

New Coding Language Model

The idea is that instead of a sequential context window, it uses a diffusion model to come up with the global structure of the program.

Vibe Coding in Xcode 26

We made an experimental Inventory app on macOS 26 and Xcode 26.

Some thoughts:

  • It put together mostly working code.
  • It made some unfortunate design decisions (user defaults storage, monolithic file to store everything)
  • We were able to correct the problems though it did put some things in the wrong file and forgot to inject all of the dependencies.

2025.06.28

New Beta Available

There is a new beta available for 26.

For new toolchains there is an excellent tool called Swiftly:

Optimizing Animation

Allen had a question about animating sticks and spheres using SceneKit. The spheres are animating smoothly while the sticks attached to the spheres would jump into place at the end. The question was how to make it smooth. The issue was that he was rendering the sticks using billboards. The consensus recommendation was to use simple geometry (e.g. hexagonal rod) to render the sticks and let the system handle it for you.

Aside: SceneKit has been deprecated in favor of Reality kit.

Result Building Arrays and Sets

Josh notes that in many of his projects he defines a result builders to declaratively define arrays.

Here is the result builder:

Here is how you do it for array:

@resultBuilder
struct ArrayBuilder<Element> {
    typealias Expression = Element
    typealias Component = Array<Element>
    
    static func buildBlock(_ components: Component...) -> Component {
        buildArray(components)
    }
    static func buildExpression(_ expression: Expression) -> Component {
        [expression]
    }
    static func buildExpression(_ expression: Expression?) -> Component {
        expression.map { [$0] } ?? []
    }
    static func buildOptional(_ component: Component?) -> Component {
        component ?? []
    }
    static func buildEither(first component: Component) -> Component {
        component
    }
    static func buildEither(second component: Component) -> Component {
        component
    }
    static func buildArray(_ components: [Component]) -> Component {
        components.flatMap { $0 }
    }
    static func buildLimitedAvailability(_ component: Component) -> Component {
        component
    }
}

An extension improves the ergonomics.

extension Array {
    static func build(@ArrayBuilder<Element> _ make: () -> Self) -> Self {
        make()
    }
}

You can use it like this:

var b = true
var c: Int? = nil
let a = Array<Int>.build {
    1
    2
    c
    if b {
        3
    }
    if #available(iOS 26.0, *) {
        4
    }
}

Another compelling use case for result builders is declarative tests:

Either and Functional Programming

A functional library for swift:

Layout and SwiftUI

Swift and Certification

If you have background in CS, it is hard to beat Paul Hegerty's class even thought it is a little old.

A starting point for a lot of people:

There are degrees you can get through coursera and udacity but consensus says a github account with the portfolio project is great. Meta has a certificate as well.

AI Coding

Sharing GRDB

All of the free episodes for pointfree are here:

Link Roundup

Android Swift Workgroup

Foundation Models


2025.06.21

Embedded Swift

Ray is embarking on a Embedded Swift hobby project to build a one button remote that toggles the mute on his Sony TV. He is using an ESP32C6 dev kit from espressif.

There is some good documentation for Embedded Swift:

Swiftly is a good tool for managing toolchains:

A previous exploration by Carlyn:

AI and Machine Learning

Software 3.0 by Andrej Karpathy

More talks:

Also,

Get a job

ExtensionKit

Expensive Runtime

What's new in SwiftUI

Josh took us through many of the SwiftUI changes presented by Paul Hudson.

With the new Liquid Glass theme, you might want to checkout the HIG again.

Learning Metal

Josh tells Ed to watch this!

Also, squircles...


2025.05.31

We discussed some other meetups:

let chars: [Character] = ["a", "ä"]
extension Character {
    func isSemanticallyEquivalent(to other: Character) -> Bool {
//        String(self).compare(String(other), options: [.diacriticInsensitive, .caseInsensitive, .widthInsensitive], locale: .current) == .orderedSame
        String(self).folding(options: [.diacriticInsensitive, .caseInsensitive, .widthInsensitive], locale: .current) == String(other).folding(options: [.diacriticInsensitive, .caseInsensitive, .widthInsensitive], locale: .current)
    }
}
print(chars[0].lowercased() == chars[1].lowercased())
print(chars[0].isSemanticallyEquivalent(to: chars[1]))
```swift
Then how to solve the problem: First the indexing solution which shows how RandomAccessCollection works.  YUou should no use this solution in any language since index math is unsafe:
```swift
extension RandomAccessCollection where Element == Character  {
    var isPalindrome: Bool {
        let backwards = reversed()
        var front = indices.first
        var back = backwards.indices.first
        while let unwrappedFront = front, let unwrappedBack = back {
            front = self[unwrappedFront...].firstIndex(where: \.isLetter)
            back = backwards[unwrappedBack...].firstIndex(where:\.isLetter)
            guard let unwrappedFront = front, let unwrappedBack = back  else { return true }
            guard self[unwrappedFront].isSemanticallyEquivalent(to: backwards[unwrappedBack]) else {
                return false
            }
            front = front.map { self.index(after: $0) }
            back = back.map { backwards.index(after: $0)}
        }
        return true
    }
}

Next the iterator solution:

extension BidirectionalCollection where Element == Character {
    var isPalindrome2: Bool {
        var frontIterator = makeIterator()
        var backIterator = reversed().makeIterator()
        var matchCount = 0
        let length = count / 2
        while let front = frontIterator.next(where: \.isLetter), let back = backIterator.next(where: \.isLetter), matchCount < length  {
            guard front.isSemanticallyEquivalent(to: back) else {
                return false
            }
            matchCount += 1
        }
        return true
    }
}

private extension IteratorProtocol where Element == Character {
    mutating func next(where predicate: (Character) -> Bool) -> Character? {
        while let next = next(){
            if predicate(next){
                return next
            }
        }
        return nil
    }
}

And finally the functional solution:

extension String {
    var isPalindrome3: Bool {
        zip(
            lazy.compactMap { $0.isLetter ? $0 : nil },
            reversed().lazy.compactMap { $0.isLetter ? $0 : nil }
        ).prefix(count/2).allSatisfy { $0.isSemanticallyEquivalent(to: $1) }
    }
}

2025.05.24

Discussion about testing, specifically about testing Combine.

Carlyn gave us a Swift testing demo and it is awesome. We also went over how to get code coverage numbers in Xcode.

Josh talked about how many

10:33:45 From Peter Wu to Everyone: For larger code bases codecov is nice that you can see the lines not covered in a PR https://about.codecov.io

A previous project from Josh (before concurrency) forming a nice example about how to get the buisness logic out of your tests.

Discussion of AI and Coding Tools

11:05:51 From carlyn to Everyone: https://www.cookiecutter.io

New Swift Playground

From Bob: This is newly released from Marcin Krzyzanowski: a third-party “Swift Playground”. https://notepadexe.com/ Looks like fun.


2025.05.17

Discussion on Apple Developer Ecosystem

  • General longing for the simple early days and the wonders of getting new stuff at WWDC.
  • Apple behind in server tech? Swift could be a great solution.
  • Widgets are not in a great state. Hoping for improvements at WWDC. (Problems around multiprocessing and controlling when they wake up.)

Swift Proposals

Progress

Using OpenAPI Generator

Keyboard Avoidance

Dependency Management PointFree

A demo of using the dependency management system from pointfree

Dependency Management

Alex showed us a dependency injection tool he created.

Programming Font

Swift Easter Egg: Jabberwocky


2025.05.10

Upcoming Events

Swift Concurrency is Awesome

Distributed Actors

Looking for examples.

Example (non-production, proof of concept): https://github.com/franklefebvre/DistributedActors-FrenchKit

Experiments with containerRelativeFrame

available length = ((container length - safe area) - (spacing x (count - 1))       
column length = available length / count
       
view length = (column length x span) + ((span - 1) x spacing)

Here is the code we experimented with:

    struct ContentView: View {
        var body: some View {
      ScrollView(.horizontal) {
       LazyHStack(spacing: 100) {
        ForEach(0..<20) { item in
         Rectangle()
          .fill([Color.red, .green, .blue][item % 3])
    //      .aspectRatio(3.0 / 2.0, contentMode: .fit)
          .frame(height: 100)
    //      .overlay(Color.yellow.opacity(0.5))
          
          .containerRelativeFrame(
           .horizontal, count: 3, span: 1, spacing: 100)
    //      .overlay(Color.blue.opacity(0.5))
        }
       }
      }
    //  .safeAreaPadding(.horizontal, 20.0)
        }
    }
    
    #Preview {
        ContentView()
    }

What is a Crash?

A cool low level exploration:


2025.05.03

What's New in Swift 6.1

Deep Dish Videos

Swift Mocking

A mocking library announced at Deep Dish from Peter's company fetch-rewards!

SSE Server Streaming Events

Carlyn's experiments on the subject.


2025.04.26

Concurrency Changes

https://swiftpackageindex.com/fetch-rewards/swift-mocking

Peter took us on a blog post tour:

Better Sounds

Exploring the sounds that are available:

    import AVFoundation
    var greeting = "Hello, playground"
    
    let systemSoundIDs: [SystemSoundID] = Array(1000...4000).map { SystemSoundID($0)}
    
    for id in systemSoundIDs {
    //    AudioServicesPlaySystemSoundWithCompletion(id) {
    //        print(id)
    //        return
    //    }
        AudioServicesPlaySystemSound(id)
        AudioServicesPropertyID(systemSoundIDs[0])
    }

Tutorial for creating system sounds (with a funny ending):

Haptics might be a useful semantic way to get some sounds:

Backups

"321" - Three copies of your data, two formats, one offsite.

"Just use Backblaze."


2025.04.19

Using Ignite

Paul Hudson's static website builder, Ignite, continues to be awesome to use. Mihaela

Talked about how to use your own custom domain.

Useful for debugging DNS issues:

https://dnschecker.org/

try! Swift Tokyo Conference

Along with some of Frank's picks:

Learning Regex:

Example: let regex = /^([1-9]\d{0,2}(,\d{3})|([1-9]\d))(.\d{2})?$/

From Apple:

Why Chris Eidhof avoids Group

SwiftUI works more consistently if you have a single stable view at the root of your hierarchy.

SwiftUI and Identity

// Single identity
// .clear option becomes a no-op 
ViewWithState()
    .background(flag ? .red : .clear)
    .animation(.easeInOut(duration: 2), value: flag)

As Josh points out, you still get the space taken.

struct ContentView: View {
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundStyle(.tint)
            Text("Hello, world!").opacity(0.0)
            Text("a")
        }
        .padding()
    }
}

New Development Environments (Design tools)

Firebase

Learning Xcode Instruments

A new cohort of the epic instruments workshop is going to run in May:

Presentation: Removing Combine

Josh started a presentation about the tooling that you can build in order to ditch import Combine.

Tools we reviewed this week:

  • Subject
  • Subscriptions
  • ViewModel

One more abstraction to build next week and we will be ready to convert apps.

Source code TBD.


2025.04.12

Async Discussion

When should you dispatch to the background?

No clear rule. Measure. An example from Josh:

import SwiftUI

@Observable
@MainActor
final class VM {
    var names: [String] = []
    func sort() async {
        async let sortedNames = { [names] in
            names.sorted()
        }()
        names = await sortedNames
    }
}

Peter brings up the point about re-entrancy. That leads to a discussion about implementing switch map.

https://rxmarbles.com

  • FlatMap in Combine is Merge Map
  • FlatMap in AsyncAlgorithms is concatMap

There are many ways to flatten such as exhaustMap.

Here is a manual way of doing it:

@Observable
@MainActor
final class VM {
    var names: [String] = []
    var sortTask: Task<[String], Error>?
    func sort() async {
        sortTask?.cancel()
        let sortTask = Task.detached { [names] in
            try await Task.sleep(for: .seconds(1))
            return names.sorted()
        }
        self.sortTask = sortTask
        do {
            names = try await sortTask.value
        } catch {
            switch error {
            case is CancellationError: print("task was canceled")
            default: print(error.localizedDescription)
            }
        }
    }
}

Xcode Preview Bug

Ed filed feedback on Xcode previews always flipping back to their default (half screen) size.

Benchmarking

How can you be sure that a better abstraction isn't slowing down your code too much. How can you measure it? It is a hard problem.

Swift Evolution Proposal Review

Yielding Accessors

Observed

Swift Forums

There is a lot of detailed, amazing information on them. Peter wonders where they get the time to make these long, detailed posts.

You can find the pitches (pre-proposal stage) on the forums:

https://forums.swift.org/c/evolution/pitches/5


2025.04.05

Discussion on SwiftData

Some key debugging tips:

You can see the underlying SQL commands: -com.apple.CoreData.SQLDebug 1

10:12:17 From Josh Homann to Everyone: https://www.hackingwithswift.com/quick-start/swiftui/how-to-dismiss-the-keyboard-for-a-textfield

10:13:21 From Josh Homann to Everyone: https://developer.apple.com/documentation/uikit/synchronizing-documents-in-the-icloud-environment

10:18:42 From Ray Fix to Everyone: https://www.pointfree.co/blog/posts/168-sharinggrdb-a-swiftdata-alternative

New in iOS 18 there are notifications for SwiftData that you can lisen to.

Recommended reading for SwiftData:

Modular Apps

Mihaela MJ is working on a modular framework. Here is a working sample project

https://github.com/mihaelamj/Formidabble

Design Patterns

It is always possible to convert between level and edge based events.

    Publishers.Merge(
        NotificationCenter.default.publisher(for: NSApplication.willResignActiveNotification).map { _ in false },
        NotificationCenter.default.publisher(for: NSApplication.willBecomeActiveNotification).map { _ in true }
    )
    .prepend(true)

Looking deeper at Patterns with Josh (such as the iterator pattern):

extension Sequence {
  static func +<Right: Sequence>(lhs: Self, rhs: Right) -> ConcatenatedSequence<Self, Right, Element> {
    ConcatenatedSequence(leftSequence: lhs, rightSequence: rhs)
  }
}
    
struct ConcatenatedSequence<Left: Sequence<Value>, Right: Sequence<Value>, Value>: Sequence {
  typealias Element = Value
  var leftSequence: Left
  var rightSequence: Right
  func makeIterator() -> Iterator {
      .init(leftIterator: leftSequence.makeIterator(), rightIterator: rightSequence.makeIterator())
  }
  struct Iterator: IteratorProtocol {
      typealias Element = Value
      var leftIterator: Left.Iterator
      var rightIterator: Right.Iterator
      mutating func next() -> Value? {
          leftIterator.next() ?? rightIterator.next()
      }
  }
}
    
let a = [1,2,3] + Set([4,5,6])
for value in a {
    print(value)
}
extension Sequence {
  static func +(lhs: Self, rhs: some Sequence<Element>) -> some Sequence<Element> {
    sequence(
        state: (
                leftIterator: lhs.makeIterator(),
                rightIterator: rhs.makeIterator()
        )
    ) { state in
          state.leftIterator.next() ?? state.rightIterator.next()
    }
}

2025.03.29

Instruments

A new video tutorial from Apple about instruments. 90 minutes long.

Swift 6: What am I missing?

If you go to the evolution website:

You can apply the filters "Implemented" and then the versions you are interested in such as Swift 6, Swift 6.1, Swift 6.2 to see what features are available.

Vibe Coding

An interesting article (via Josh) about a potential future for the industry.

Gemini 2.5 model is new model that does a lot of stuff correctly that other models don't seem to be able to.

Someone mentioned that Claude is good for interactive Vibe coding.

Presentation: GoF: Design Patterns in Swift

Josh H. presented about the classic "Gang of Four" Design patterns--it is a common lexicon that can be used to communicate with other developers or even machines using prompts.

Creational Patterns

These focus on object creation mechanisms, promoting better encapsulation and system independence.

  • Factory Method: Defines an interface for creating objects, letting subclasses decide the class to instantiate.
  • Abstract Factory: Provides an abstract factory class that creates families of related objects.
  • Builder: Separates object construction from its representation, enabling different representations through the same process.
  • Prototype: Creates objects by cloning existing instances rather than using constructors.
  • Singleton: Ensures a class has only one instance and provides global access to it.

Structural Patterns

These focus on how classes and objects can be composed to form larger structures.

  • Adapter: Converts an interface to another, allowing incompatible classes to work together.
  • Decorator: Dynamically adds responsibilities to objects by wrapping them in decorator classes.
  • Proxy: Acts as a placeholder for an object, controlling access and handling tasks like lazy loading.
  • Facade: Provides a simplified interface to a complex system, reducing exposed complexity.
  • Composite: Treats individual and composite objects uniformly, useful for tree structures.
  • Flyweight: Shares data among multiple objects to reduce memory usage.

Behavioral Patterns

These focus on how classes interact and the responsibilities of objects within a system.

  • Strategy: Encapsulates algorithms, allowing selection at runtime based on conditions.
  • Observer: Establishes a one-to-many dependency, notifying dependents of state changes.
  • State: Changes object behavior when its internal state changes.
  • Chain of Responsibility: Passes requests along a chain of handlers until one handles it.
  • Command: Encapsulates requests as objects for operations like logging and undo/redo.
  • Iterator: Accesses aggregate elements sequentially without exposing the structure.
  • Mediator: Reduces coupling between classes by encapsulating interactions.
  • Memento: Captures and restores object state, useful for undo/redo functionality.
  • Visitor: Adds methods to objects without changing their classes, useful for operations across sets of objects.
  • Template Method: Defines an algorithm skeleton in a base class, letting subclasses implement specific steps.

Other Patterns

Peter mentioned a pattern that he has been looking into

Alex mentioned one of his favorites: "Inversion of Control 🙂"


2025.03.22

Discussion

Ray shared a recent experience with upgrading XCTest to Swift Testing. The migration was straightforward in Xcode and there is a good migration guide.

Swift Evolution

Related to Swift testing, this proposal means you can use a short description with spaces and punctuation as the name of your test:

Josh also taked about a new addition to the language which lets you better hide implementation details.

Swift Packages

Talked about modularization.

You can set the library type to statically or dynamically link packages.

https://developer.apple.com/documentation/packagedescription/product/library(name:type:targets:)

CoreData in packages needs to be handled specially: https://ishabazz.dev/blog/2020/7/5/using-core-data-with-swift-package-manager

Other discussion

From Ed returning integers as a blob of bytes:

extension FixedWidthInteger {
  var data: Data {
    let data = withUnsafeBytes(of: self) { Data($0) }
    return data
  }
}

From Josh (another way to deserialize)

enum JSON {
    indirect case array([JSON])
    indirect case dictionary([String: JSON])
    case boolean(Bool)
    case number(Double)
    case string(String)
    case null
}
    
extension JSON: Decodable {
    init(from decoder: Decoder) throws {
        self = try Result { try decoder.singleValueContainer() }
            .flatMap { container in
                container.decodeNil()
                    ? .success(JSON.null)
                    : Result { JSON.boolean(try container.decode(Bool.self)) }
                        .flatMapError { _ in Result { JSON.number(try container.decode(Double.self)) } }
                        .flatMapError { _ in Result { JSON.string(try container.decode(String.self)) } }
                        .flatMapError { _ in Result { JSON.array(try container.decode([JSON].self)) } }
                        .flatMapError { _ in Result { JSON.dictionary(try container.decode([String: JSON].self)) } }
             }.get()
    }
}

2025.03.15

Discussion

How to cleanup space on your disk.

Daisydisk

Hyperspace to delete duplicates

OmniDiskSweeper

Presentation: Recent Swift Evolution Topics

MutableSpan: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0467-MutableSpan.md

To understand borrowing and consuming we looked at some of the

static func embiggened(_ value: [Int]) -> [Int] {
    var mutableValue = value
    for index in mutableValue.indices {
        mutableValue[index] += 1
    }
    return mutableValue
}

static func embiggened(byBorrowing value: borrowing [Int]) -> [Int] {
    var mutableValue = copy value
    for index in mutableValue.indices {
        mutableValue[index] += 1
    }
    return mutableValue
}
static func embiggen(byExclusiveBorrow value: inout [Int]) {
    for index in value.indices {
        value[index] += 1
    }
}
static func embiggen(byConsuming value: consuming [Int]) -> [Int] {
    for index in value.indices {
        value[index] += 1
    }
    return consume value
}
    
let a = [1, 2, 3]
let b = embiggen(byConsuming: a)
print(a,b)

Presentation: Replacing Combine with Modern Concurrency

Josh continued with a topic from last week about how to replace combine with modern Swift concurrency. This week Josh created a Pipe abstraction to allow multiple observers of a sent value (much like notification center).

import Foundation
import Synchronization
    
public final class Pipe<Value: Sendable>: Sendable, AsyncSequence {
    public typealias Stream = AsyncStream<Element>
    public typealias AsyncIterator = Stream.AsyncIterator
    public typealias Element = Value
    private let lockedContinuations: Mutex<[UUID: Stream.Continuation]>
    private let replayCount: Int
    public init(replay: Int = 0) {
        replayCount = replay
        lockedContinuations = .init([:])
    }
    deinit {
        lockedContinuations.withLock { continuations in
            continuations.values.forEach { $0.finish() }
        }
    }
    public func send(_ value: Value) {
        lockedContinuations.withLock { continuations in
            continuations.values.forEach { $0.yield(value) }
        }
    }
    
    public func makeAsyncIterator() -> AsyncIterator {
        let (stream, continuation) = Stream.makeStream(of: Element.self,
                bufferingPolicy: .bufferingNewest(replayCount))
        let id = UUID()
        continuation.onTermination = { [weak self] _ in
            self?.lockedContinuations.withLock { $0[id] = nil }
        }
        lockedContinuations.withLock { $0[id] = continuation }
        return stream.makeAsyncIterator()
    }
}

2025.03.08

Instruments

James Dempsey has a new online course that teaches the ins and outs of Apple Instruments:

Also, there is a new Processor Trace Instrument that uses low level hardward on the M4 and iPhone 16 to collect very detailed metrics about performance.

https://developer.apple.com/documentation/xcode/analyzing-cpu-usage-with-processor-trace?changes=la

OpenAI on Xcode

Chatgpt Xcode: https://help.openai.com/en/articles/10119604-work-with-apps-on-macos

Running a VM on macOS

macOS VM: https://arstechnica.com/gadgets/2022/07/how-to-use-free-virtualization-apps-to-safely-test-the-macos-ventura-betas/

Syncing across devices

URL Encoding Game State

Ways to crunch down the JSON so it can be URL encoded.

Using a server:

A large infrastructure:

Smaller deployments:

Industry Compensation

Presentation (Josh): Removing @StateObject

Previously, Josh demostrated how SwiftUI can use Combine to achieve lazy initialization and prevent repeated re-initialization of a view model of a view in a timeline view using StateObject.

Inspired by this blog post:

Josh created a special property wrapper ViewModel that does the same thing and means you don't need to use StateObject or derive from Combine's ObservableObject.

import SwiftUI
import PlaygroundSupport
    
PlaygroundPage.current.setLiveView(ContentView())
    
struct ContentView: View {
    var body: some View {
        TimelineView(.periodic(from: .now, by: 3)) { _ in
            let _ = print("-----------\(Date.now)-----------")
            V()
        }
    }
}
    
struct V: View {
    var vm = VM("naked var")
    @State var vm0 = VM("@State")
    @ViewModel var vm1 = VM("@ViewModel")
    var body: some View {
        Text(vm.text)
        Text(vm0.text)
        Text(vm1.text)
    }
}
    
@Observable
final class VM {
    var text: String
    init(_ text: String) {
        self.text = text
        print("Init \(text)")
    }
    deinit {
        print("Deinit \(text)")
    }
}

Here is the property wrapper:

@propertyWrapper
struct ViewModel<Wrapped: AnyObject & Observable>: DynamicProperty {
    @State private var reference: Reference
    var wrappedValue: Wrapped {
        reference.value
    }
    var projectedValue: Bindable<Wrapped> {
        reference.bindable
    }
    init(wrappedValue make: @autoclosure @escaping () -> Wrapped) {
        _reference = State(wrappedValue: Reference(make))
    }
}
    
extension ViewModel {
    final class Reference {
        private var viewModel: Wrapped?
        private let makeViewModel: () -> Wrapped
        lazy var value: Wrapped = makeViewModel()
        lazy var bindable = { Bindable(value) }()
        init(_ make: @escaping () -> Wrapped) {
            makeViewModel = make
        }
    }
}

2025.03.01

FOSDEM

There were a lot of Swift topics presented at this years FOSDEM (Free Open Source Developers European Meeting) that have been organized for viewing here:

SwiftUI Fundamentals

A new book about SwiftUI from Natalia Panferova of nilcoalecing is available. Bob DeLaurentis says that it does a great job in explaining some of the subtleties of the framework.

Electronic Readers

This led to a discussion about books and apps for reading ebooks.

Forward Progress and Concurrency

Led by Peter and Josh, we discussed concurrency and the tools to guarantee forward progress.

To visualize / test restricting cooperative thread pool: https://alejandromp.com/development/blog/limit-swift-concurrency-cooperative-pool/

A key insight is the ability to use LIBDISPATCH_COOPERATIVE_POOL_STRICT=1 to make sure forward progress is always being made.

This is the thread on forum that goes in-depth on lock ownership and safety in using across suspension point https://forums.swift.org/t/incremental-migration-to-structured-concurrency/54939/5

Assert that you are on a specific queue https://developer.apple.com/documentation/dispatch/dispatchprecondition(condition:)

10:33:29 From Mihaela MJ to Everyone: https://github.com/siteline/swiftui-introspect

New Isolation Guarantees

Josh gave us a tour of proposal 0461 and how it makes things more similar between sync and async methods.

import Foundation
    
final class Q {
    func sync() {
        MyActor.assertIsolated()
        print("synchronous Q")
    }
    nonisolated func nonSync() async {
        MyActor.assertIsolated()
        print("asynchronous Q")
    }
}
    
@MyActor
final class P {
    var q = Q()
    func doStuff() async {
        q.sync()
        async let v = q.nonSync()
        let _ = await v
    }
}
    
@globalActor
actor MyActor: GlobalActor {
    static let shared = MyActor()
    private init() { }
}
    
Task {
    let p = await P()
    await p.doStuff()
}

Making UI around Command line

Some useful tools!

Also, some code from Carlyn:

@discardableResult
func shellOneShot(_ command: some StringProtocol) throws -> String {
   let task = Process()
   let pipe = Pipe()
       
   task.standardOutput = pipe
   task.standardError = pipe
   task.arguments = ["-c", command]
       
   //task.currentDirectoryURL
   //task.qualityOfService
   //task.environment = ProcessInfo.processInfo.environment
       
   task.standardInput = nil
   task.executableURL = URL(fileURLWithPath: "/bin/zsh")
   try task.run()
       
   let data = pipe.fileHandleForReading.readDataToEndOfFile()
   let output = String(data: data, encoding: .utf8)!
       
   task.waitUntilExit()
    
   if task.terminationStatus == 0 || task.terminationStatus == 2 {
     return output
   } else {
     print(output)
     throw CustomProcessError.unknownError(exitCode: task.terminationStatus)
   }
}

@discardableResult
func runProcess(_ tool:URL, arguments:[some StringProtocol] = [], workingDirectory:URL? = nil) throws -> String {
    let task = Process()
    let pipe = Pipe()
        
    task.standardOutput = pipe
    task.standardError = pipe
    task.arguments = arguments
        
    if let workingDirectory {
        task.currentDirectoryURL = workingDirectory
    }
    //task.qualityOfService
    //task.environment
    
    task.standardInput = nil
    task.executableURL = tool
    try task.run()
        
    let data = pipe.fileHandleForReading.readDataToEndOfFile()
    let output = String(data: data, encoding: .utf8)!
        
    task.waitUntilExit()
    
    if task.terminationStatus == 0 || task.terminationStatus == 2 {
      return output
    } else {
      print(output)
      throw CustomProcessError.unknownError(exitCode: task.terminationStatus)
    }
    }

enum CustomProcessError: Error {
  case unknownError(exitCode: Int32)
}

Apple Vision Pro Meeting

Apparently there was an interesting (mostly non-code meeting) by Apple last week about how to develop "experiences" of vision pro. Ed atteneded online and John B was there in-person. Four sessions over the course of the day.

Apparently they had a demo where they showed how to render the experience of being on the moon.

    Environment metrics
    Geometry: 20K-500K total triangles
    View: -100K triangles per camera angle
    Memory: Less than 250MB total texture data
    Entities: Under 200 draw calls
    Materials: Unlit with custom shader effects

2025.02.22

Concurrency in Legacy Code

Questions from Rainer about how to use concurrency effectively in legacy code.

We talked about where things run and how the default might change in future versions of Swift.

Link from Peter:

Examples from Josh:

struct A {
    //@MainActor
    nonisolated func a() {
        Task {
            MainActor.assertIsolated()
        }
    }
}

Task {
     A().a()
}

Instead of falling back to using Combine (which bring tech debt with it), a suggestion from Josh to make simple abstractions to avoid creating Tasks everywhere.

extension AsyncSequence where Failure == Never {
    func subscribe<Unretained: AnyObject>(withUnretained object: Unretained, onNext: @escaping (Unretained, Element) -> Void) -> Task<Void, Never> {
        Task { [weak object] in
            for await value in self {
                guard let object else { return }
                onNext(object, value)
            }
        }
    }
}

Can be used like this:

actor A {
    var subscription: Task<Void, Never>?
     func a()  {
         let (output, input) = AsyncStream.makeStream(of: Int.self, bufferingPolicy: .bufferingNewest(1))
         subscription = output.subscribe(withUnretained: self) { unretained, value in
             print(unretained, value)    
         }
         input.yield(1)
         input.yield(2)
         input.yield(3)
    }
}

Protocol Composition

File System

This came up in the context of Peter's custom image caching problem.

Some notes from Carlyn:

Using custom executors (Josh)

actor LandingSite {
    private let queue = DispatchSerialQueue(label: "something")

    nonisolated var unownedExecutor: UnownedSerialExecutor {
        queue.asUnownedSerialExecutor()
    }
    
    func acceptTransport(_ transport: PersonalTransportation) {
        // this function will be running on queue
    }
}

10:35:09 From carlyn to Everyone: private func appendData(data: Data) throws { let fileHandle = try FileHandle(forWritingTo: storageUrl) fileHandle.seekToEndOfFile() fileHandle.write(data) fileHandle.closeFile() }

Creating a custom global actor:

@globalActor actor SharedActor {
  static let shared = SharedActor()
}
    
@SharedActor final class A { }
@SharedActor final class B { }

Delphi Style Components

Starting with Package definitions. From MJ

// Dependency grouping
enum Dependencies {
    static var common: [Target.Dependency] {
        [
            .product(name: "Difference", package: "Difference"),
            .product(name: "LifetimeTracker", package: "LifetimeTracker"),
        ]
    }
}

    .target(
         name: "SharedModels",
         dependencies:
            Dependencies.common + [
                "AutomaticSettings",
            ]
    ),

These ideas come from:

Related:

@_exported import PackageName

For details see:

Interesting Links

Ed's Hex Tac Toe in Beta Review

Hex Tac Toe is waiting for Apple beta review. Anyone else want to be on the beta, send your Apple email (DM). I have all the ones from before.


2025.02.15

Peter worked on a caching system for an app and wanted feedback for how it could be improved. The basic app is here:

https://github.com/PeterWu9/Recipes

A recommendation from Josh to use the headers in the HTTP response headers:

Also related:

SwiftUI View Builders

View builder returns some View which is a container view like ConditionalContent.

struct W: View {
    let a: Int
    @ViewBuilder
    var body: some View {
        switch a {
        case ..<0: Text("Negative")
        case 0: Text("Zero")
        case 1... : Color.green
        default: EmptyView()
      }
    }
}

SwiftUI Navigation Bug

Joe shared this SwiftUI navigation bug:

struct ContentView: View {
    var body: some View {
        NavigationStack {
            NavigationLink {
                Text("Do not tap back, you'll regret it")
                    .toolbarBackground(.visible, for: .navigationBar)
                    .toolbarBackground(Color.orange, for: .navigationBar)
                    .toolbarColorScheme(.dark, for: .navigationBar)
            } label: {
                VStack {
                    Text("First page is the sweetest")
                }               
                .padding()
            }
            .navigationTitle("First Page")
            .toolbarBackground(.visible, for: .navigationBar)
            .toolbarBackground(Color.orange, for: .navigationBar)
            .toolbarColorScheme(.dark, for: .navigationBar)
        }
    }
}

Sequencing Animations

Sample from Josh:

    withAnimation(animation, completionCriteria: .logicallyComplete) {
                operation()
            } completion: {
                continuation.resume()
            }

Swift Blog gRPC 2


2025.02.08

Discussions

Assistive Technology

09:57:53 From Josh Homann to Everyone: Request sharing in FaceTime: https://support.apple.com/guide/iphone/request-give-remote-control-a-facetime-call-iph5d70f34a3/ios

Hacking Problem

Puzzle from John Brewer

var test3 = [-1, 1, 2, 3, 4, -1, -9, -6, 10, 1, -5]
print(largestSumSpan(array: test3)) // [10, 1]
var test4 = [-1, 1, 2, 3, 4, -1, -9, -6, 8, 1, -5]
print(largestSumSpan(array: test4)) // [1, 2, 3, 4]

What is the best idiomatic Swift to handle this?

print(missingPair([1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 7, 6, 5, 4, 3, 2, 1]))
9

There is a "trick" to this one which to xor all of the bits.

Related book


2025.02.01

Resources and SwiftPM

  • Peter was able to make a common target that other targets could depend on.

Exciting announcement Carlyn let us know about is that Swift Build (used by Xcode) is, as of today, an open source technology. It will support Windows and Linux.

Also, the official announcement:

https://www.swift.org/blog/the-next-chapter-in-swift-build-technologies/

We looked at conditional package inclusion. You can conditionalize based on platform but it doesn't look like there are too many other options.

An example from Mihaela:

    .product(name: "ResChatHouUIKit", package: "ResChatHouUIKit", condition: .when(platforms: [.iOS])),
                    .product(name: "ResChatUIKit", package: "ResChatUIKit", condition: .when(platforms: [.iOS])),
                    .product(name: "ResChatHouAppKit", package: "ResChatHouAppKit", condition: .when(platforms: [.macOS])),
                    .product(name: "ResChatAppKitUI", package: "ResChatAppKitUI", condition: .when(platforms: [.macOS])),
                    ]
            ),

Swift Playgrounds App

Now supports Swift 6. You may need to remove previous versions.

Hex Tac Toe

Ed is working on his game hex-tac-toe. Lots of suggestions for how to improve.

Other random stuff

It is an open source airdrop thing but written in Flutter:

Josh demo'ed LLMs including deepseek on your local machine:

You can run in from within a UI too that supports many models.


2025.01.25

  • How to share resources among multiple Swift packages.
  • Continued working on Mine Sweeper

2025.01.18

Making CoW Types Sendable

We modernized a small app to Swift 6. Making a type CoW and isolating all mutation allows you to declare a type sendable. The mutable state is not shared so these properties can be marked sendable (unsafe).

Performance

Measure the performance of a new collection type using the Swift performance package.

https://www.swift.org/blog/benchmarks/

Mine Sweeper

Continued the mine sweeper example.


2025.01.10

Debugging SceneKit Audio

We spent some time debugging a problem with a SceneKit app that was leaking audio players.

Animating Text

Rainer was trying to animate text along a Bézier curve.

Josh reminded us of this project example from last year:

Also, a Primer on Bézier curves: https://pomax.github.io/bezierinfo/

Rainer noted that the example he was trying to emulate:

His code here:

Continuing the Mine Sweeper Example

This week we looked at a serious performance problem in what we built so far. Changing the color of a handful of tile taking almost a second to perform.

A nice video from Ben Cohen about Fast Safe Mutable State:

An internal way to do in-place mutation:

But the biggest performance win was had by using a properly identifiable type in the ForEach statement of the grid.


2025.01.03

A functional solution to Ed's game

  • Ed is making a game where he needs to match n distinct items in a row. Josh offered a solution in functional swift:
  • We start with an assumption that we have a node, a sequence of adjacencies that can transform a node into another node, and the number of distinct items we want to match
let n = 6
let adjacency: some Sequence<(Node) -> Node?>
let node: Node

layout

  • We can express the algorithm we want to apply as a node represents a winning state if there is a way to transform it into a sequence of n distinct elements. or more formally: layout
  • Equivalently in swift:
let isWon = adjacency.contains { [node, n] adjacency in
	Set(sequence(first: node) { adjacency(node)}.prefix(n))).count == n
}
  • We can further express our intent with the use of explanatory variables:
let isWon = adjacency.contains { [node, n] adjacency in
    let adjacentUnique = Set(sequence(first: node) { adjacency(node)}.prefix(n))
	let isWinningRun = adjacentUnique.count == n
	return isWinningRun
}
  • Ed asked how to get the winning nodes. We can do this by changing the operator to compactMap:
let winningSets = adjacency.compactMap { [node, n] adjacency in
    let adjacentUnique = Set(sequence(first: node) { adjacency(node)}.prefix(n))
	let isWinningRun = adjacentUnique.count == n
	return isWinningRun ? adjacentUnique : nil
}
.reduce(into: Set<Node>()) { $0.formUnion($1) }

Mine sweeper continued

  • We updated the Layout to use a cache and added a viewModel:
@Observable
final class ViewModel: ObservableObject {
    private(set) var cells: Grid2D<Cell> = .init(rows: 8, columns: 8) { x, y in
        .init(id: x + y * 8, position: .init(x, y), color: .allCases.randomElement()!)
    }
    func tap(index: SIMD2<Int>) {
        cells[index].color = .blue
    }
}

struct Cell: Identifiable, Hashable {
    let id: Int
    let position: SIMD2<Int>
    var color: ViewModel.BoardColor
}

extension ViewModel {
    enum BoardColor: Hashable, CaseIterable {
        case red, green, blue
    }
}

struct ContentView: View {
    @StateObject private var viewModel = ViewModel()
    var body: some View {
        Board(dimension: 8) {
            ForEach(viewModel.cells.indices, id: \.self) { index in
                Group {
                    switch viewModel.cells[index].color {
                    case .red: Color.red
                    case .green: Color.green
                    case .blue: Color.blue
                    }
                }
                .onTapGesture {
                    viewModel.tap(index:  viewModel.cells[index].position)
                }
                .boardPosition(index)
            }
        }
    }
}

extension View {
    func boardPosition(_ position: SIMD2<Int>) -> some View {
        layoutValue(key: BoardPosition.self, value: position)
    }
    func boardPosition(x: Int, y: Int) -> some View {
        boardPosition(.init(x, y))
    }
}

struct Board: Layout {
    let dimension: Int

    func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout Cache) -> CGSize {
        let size = proposal.replacingUnspecifiedDimensions(by: .zero)
        let minimumDimension = min(size.width, size.height)
        return .init(width: minimumDimension, height: minimumDimension)
    }

    func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout Cache) {
        if cache.bounds != bounds {
            cache.bounds = bounds
            cache.cellSize = CGSize(width: bounds.width / Double(dimension), height: bounds.height / Double(dimension))
            let transform = CGAffineTransform
                .identity
                .translatedBy(x: bounds.origin.x, y: bounds.origin.y)
                .scaledBy(x: cache.cellSize.width, y: cache.cellSize.height)
            cache.grid = .init(rows: dimension, columns: dimension) { x, y in
                CGPoint(x: x, y: y).applying(transform)
            }
        }
        for view in subviews {
            let position = cache.grid[safe: view[BoardPosition.self]] ?? bounds.origin
            view.place(
                at: position,
                proposal: .init(cache.cellSize)
            )
        }
    }

    func makeCache(subviews: Subviews) -> Cache {
        .init()
    }

    struct Cache {
        var bounds = CGRect.zero
        var cellSize = CGSize.zero
        var grid: Grid2D<CGPoint> = .init(repeating: .zero, rows: 0, columns: 0)
    }
}

struct BoardPosition: LayoutValueKey {
    static let defaultValue = SIMD2<Int>.zero
}

About

Meeting minutes and learnings from the physical space meeting.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 5