<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Wesley de Groot</title>
        <link>https://wesleydegroot.nl</link>
        <description>Blog posts about programming</description>
        <atom:link href="https://wesleydegroot.nl/rss.xml" rel="self" type="application/rss+xml" />
          <item>
    <title>Safari in SwiftUI</title>    <link>https://wesleydegroot.nl/blog/safari-in-swiftui</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/safari-in-swiftui</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:33 +0200</pubDate>
    <category>SwiftUI</category>
    <description>&lt;p&gt;You started playing around with SwiftUI, and you want to present a Safari(WebView), you search in the documentation, and..., SwiftUI has no native way to display a WebView!&lt;/p&gt;
&lt;p&gt;To use Safari (SFSafari) you need to create a custom &lt;code&gt;UIViewControllerRepresentable&lt;/code&gt; which is needed to use ViewControllers in SwiftUI.&lt;/p&gt;
&lt;p&gt;This post contains, 3 sections,&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;The code in steps&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The code (complete)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Example on how to use.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Full source code of our &lt;code&gt;UIViewControllerRepresentable&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI
import SafariServices
import UIKit

/// Make a Safari View for SwiftUI
public struct SafariView: UIViewControllerRepresentable {
    typealias UIViewControllerType = SFSafariViewController

    /// Create a binding for our URL String
    @Binding var urlString: String

    /// Public initializer.
    public init(url: String) {
        _urlString = url
    }

    /// Make a UIViewController for SwiftUI.
    public func makeUIViewController(
        context: UIViewControllerRepresentableContext&amp;lt;SafariView&amp;gt;
    ) -&amp;gt; SFSafariViewController {
        // Check if the url is valid, otherwise fatalError.
        guard let url = URL(string: urlString) else {
            fatalError("Invalid urlString: \(urlString)")
        }

        // Generate the SFSafariViewController.
        let safariViewController = SFSafariViewController(url: url)

        // Optional, set int/accentcolor.
        safariViewController.preferredControlTintColor = UIColor(Color.accentColor)

        // We want our view to have a close button instead of 'done'
        safariViewController.dismissButtonStyle = .close

        // Return the view
        return safariViewController
    }

    public func updateUIViewController(
      _ safariViewController: SFSafariViewController, 
      context: UIViewControllerRepresentableContext&amp;lt;SafariView&amp;gt;) {
        return
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;How to use:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct CodeView: View {
    // whether or not to show the Safari ViewController
    @State var showSafari = false

    // initial URL string
    @State var urlString = "https://wesleydegroot.nl"

    var body: some View {
        VStack {
            Button("Open This article") {
                self.urlString = "https://wesleydegroot.nl/blog/Safari-in-SwiftUI"

                showSafari = true
            }
        }
        .popover(isPresented: $showSafari, content: {
            SafariView(urlString: $urlString)
        })
    }
}

// MARK: Preview
struct CodeView_Previews: PreviewProvider {
    static var previews: some View {
        CodeView()
            .previewLayout(PreviewLayout.sizeThatFits)
            .padding()
            .previewDisplayName("Code Preview")
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
  </item>
  <item>
    <title>Swipe actions in Swift</title>    <link>https://wesleydegroot.nl/blog/swipe-actions-in-swift</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/swipe-actions-in-swift</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:34 +0200</pubDate>
    <category>SwiftUI</category>
    <description>&lt;p&gt;The issue:&lt;/p&gt;
&lt;p&gt;Gestures, delegates, it can be a big struggle (especially for beginners).&lt;/p&gt;
&lt;p&gt;If you use a lot of gestures then you’ll need to implement it over and over.&lt;br&gt;
One of the problems is, that code what you write will not apply to other views in your application.&lt;br&gt;
If we use a lot of swipe actions, in different places within our application we want to have reusable code.&lt;br&gt;
But then we see ourselfs overloaded with issues, can it be made more easy? &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Yes!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;How can we make it more easy?&lt;/p&gt;
&lt;p&gt;Using a UIViewextension.&lt;/p&gt;
&lt;p&gt;Why not a UIViewController?&lt;br&gt;
“Because we also want to support other applications which have a &lt;code&gt;UIView&lt;/code&gt;.”&lt;br&gt;
How do we start?&lt;/p&gt;
&lt;p&gt;In this post we’ll start with a UIView extension, to make it reusable for other applications (e.g. UIImageView, UIView, ...).&lt;/p&gt;
&lt;p&gt;In my case it didn’t work on a &lt;code&gt;UITableViewController&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Psuedo code:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import Foundation
import UIKit
extension UIView {
// psuedo code continues&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We also need a variable, but simply var myVariable = ...does not work, since we are working in an extension.&lt;br&gt;
There is a workaround, and it may be more easy than you think.&lt;br&gt;
We’ll use a structand that will solve all our variable problems.&lt;br&gt;
We want to reuse the data, so we make a static var.&lt;/p&gt;
&lt;p&gt;Psuedo code:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;// .... psuedo code to above blocks&amp;lt;br&amp;gt;struct gestureClosures {
    static var up = ...
    static var down = ...
    static var left = ...
    static var right = ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We also need to create a function, to make it work!&lt;/p&gt;
&lt;p&gt;Psuedo code:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;func swipeAction(
        swipeDirection: UISwipeGestureRecognizer.Direction,
        completionHandler: @escaping ()-&amp;gt;()
        ) {
        // Add a swiper
        let swiper = .... #selector(self.invokeTarget(_:))
            // give the direction as in swipeDirection
            swiper.direction = swipeDirection
        // add to the view        
        self.addGestureRecognizer(swiper)

        // save the completionHandler
        switch swipeDirection {
        case .up:
            gestureClosures.up = completionHandler
        case .down:
            gestureClosures.down = completionHandler
        case .left:
            gestureClosures.left = completionHandler
        case .right:
            gestureClosures.right = completionHandler 
        default:
            print("Nothing")        
        }    
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But we still need to respond on the swipe actions!&lt;/p&gt;
&lt;p&gt;Yup, pseudocode:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;@objc func invokeTarget(...) {
    // disamble 
        switch swipeDirection {
        case .up:
            gestureClosures.up()
        case .down:
            gestureClosures.down()
        case .left:
            gestureClosures.left()
        case .right:
            gestureClosures.righ() 
        default:
            print("Nothing")        
        }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And if we translate it to functional swift code.&lt;/p&gt;
&lt;p&gt;Then the output looks similair to above.&lt;/p&gt;
&lt;p&gt;The complete solution:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import Foundation
import UIKit

// Extend UIView
extension UIView {
    /// Setup a struct for saving the gesture handlers
    struct gestureHandler {
        /// Gesture "Up"
        static var up: (()-&amp;gt;())? = nil
        /// Gesture "Down"
        static var down: (()-&amp;gt;())? = nil
        /// Gesture "Left"
        static var left: (()-&amp;gt;())? = nil
        /// Gesture "Right"
        static var right: (()-&amp;gt;())? = nil
    }

    /**
     * Add a swipe action to UIView
     *
     * - Parameter swipeDirection: `.up`,`.down`,`.left`,`.right`
     * - Parameter completionHandler: The completionhandler.
     */
    func swipeAction(
        swipeDirection: UISwipeGestureRecognizer.Direction,
        completionHandler: @escaping ()-&amp;gt;()
    ) {
        /// Add swipe handler
        let swiper = UISwipeGestureRecognizer(
            // UIView is the target
            target: self,

            // invokeTarget is our responder
            action: #selector(self.invokeTarget(_:))
        )

        // Set the direction of the swipe handler
        swiper.direction = swipeDirection

        // Add the gesture recognizer to the view
        self.addGestureRecognizer(swiper)

        // Switch between directions,
        // Save it to our struct.
        switch swipeDirection {
        case .up:
            // Save the completionHandler to gestureHandler.up
            gestureHandler.up = completionHandler
        case .down:
            // Save the completionHandler to gestureHandler.down
            gestureHandler.down = completionHandler
        case .left:
            // Save the completionHandler to gestureHandler.left
            gestureHandler.left = completionHandler
        case .right:
            // Save the completionHandler to gestureHandler.right
            gestureHandler.right = completionHandler
        default:
            print("Nothing")
        }
    }

    /**
     * Respond to a swipe action from UIView
     *
     * - Parameter gesture: The UIGestureRecognizer
     */
    @objc func invokeTarget(_ gesture: UIGestureRecognizer?) {
        /// Unwrap the swipeGesture
        if let swipeGesture = gesture as? UISwipeGestureRecognizer {
            // Switch between the direction
            switch swipeGesture.direction {
            case .up:
                /// Unwrap gestureHandler.up if possible
                guard let execute = gestureHandler.up else {
                    return
                }

                execute()
                break;
            case .down:
                /// Unwrap gestureHandler.down if possible
                guard let execute = gestureHandler.down else {
                    return
                }

                execute()
                break;
            case .left:
                /// Unwrap gestureHandler.left if possible
                guard let execute = gestureHandler.left else {
                    return
                }

                execute()
                break;
            case .right:
                /// Unwrap gestureHandler.right if possible
                guard let execute = gestureHandler.right else {
                    return
                }

                execute()
                break;
            default:
                print("N/A")
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
  </item>
  <item>
    <title>Easy Publishers</title>    <link>https://wesleydegroot.nl/blog/easy-publishers</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/easy-publishers</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:34 +0200</pubDate>
    <category>Swift</category>
    <category>Observable</category>
    <description>&lt;p&gt;Ever wondered how to create a simple publisher?&lt;/p&gt;
&lt;p&gt;In this blog i'll try to show you the basics&lt;/p&gt;
&lt;p&gt;Firstly we need to import the required frameworks&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;// Used for the ObservableObject
import Combine

// The easiest for publishers
import SwiftUI&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Published object&lt;/h3&gt;
&lt;p&gt;We're going to create a class what subclasses &lt;code&gt;ObservableObject&lt;/code&gt; and contains a &lt;code&gt;@Published&lt;/code&gt; variable.&lt;br&gt;
(for this example it is also using a timer to update the value)&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;class MyObject: ObservableObject {
    // This published variable will be updated several times.
    @Published var y: String

    // Set a default value to y, and start the timer
    init() {
        // Set the value of y to Loading...
        y = "Loading..."

        // Run this function after 5 seconds on the main thread.
        DispatchQueue.main.asyncAfter(deadline: .now() + 5) { 
            self.upd() // Update
        }
    }

    func upd() {
        // Set the value of y to Published World
        y = "Published World"

        // Tell the system that this ObservableObject class is updated.
        self.objectWillChange
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;(SwiftUI) View&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct MyView: View {
    // Define our object(class) as Observed
    @ObservedObject var v = MyObject()

    var body: some View {
        Text(v.y) // This will change automatically if y is changed
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Full code&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import Combine
import SwiftUI

class MyObject: ObservableObject {
    @Published var y: String
    init() {
        y = "Loading..."
        DispatchQueue.main.asyncAfter(deadline: .now() + 5) { 
            self.upd()
        }
    }
    func upd() {
        y = "Published World"
        self.objectWillChange
    }
}

struct MyView: View {
    @ObservedObject var v = MyObject()
    var body: some View {
        Text(v.y)
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
  </item>
  <item>
    <title>Observable Geocoder</title>    <link>https://wesleydegroot.nl/blog/observable-geocoder</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/observable-geocoder</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:35 +0200</pubDate>
    <category>Swift</category>
    <category>Observable</category>
    <description>&lt;p&gt;Firstly we want to &lt;code&gt;import CoreLocation&lt;/code&gt; for the location services.&lt;/p&gt;
&lt;p&gt;We're going to construct a basic class, which subclasses &lt;code&gt;ObservableObject&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;class Geocoder: ObservableObject {
/// ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We're going to add &lt;code&gt;@Published public var&lt;/code&gt; (published public variables),&lt;br&gt;
for the items we want to expose, in my case it are the following variables &lt;code&gt;placeMark&lt;/code&gt;, &lt;code&gt;location&lt;/code&gt;, &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;iso&lt;/code&gt;, &lt;code&gt;country&lt;/code&gt;, &lt;code&gt;postalCode&lt;/code&gt;, &lt;code&gt;state&lt;/code&gt;, &lt;code&gt;subState&lt;/code&gt;, &lt;code&gt;city&lt;/code&gt;, &lt;code&gt;subCity&lt;/code&gt;, &lt;code&gt;street&lt;/code&gt;, &lt;code&gt;subStreet&lt;/code&gt;, &lt;code&gt;region&lt;/code&gt;, &lt;code&gt;timeZone&lt;/code&gt;, &lt;code&gt;inlandWater&lt;/code&gt;, &lt;code&gt;ocean&lt;/code&gt; and &lt;code&gt;areasOfInterest&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Full Code:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import CoreLocation

class Geocoder: ObservableObject {
    /// Placemark
    @Published public var placeMark: CLPlacemark?

    /// Location
    @Published public var location: CLLocation?

    /// The name of the placemark.
    @Published public var name: String?

    /// The abbreviated country or region name.
    @Published public var iso: String?

    /// The name of the country or region associated with the placemark.
    @Published public var country: String?

    /// The postal code associated with the placemark.
    @Published public var postalCode: String?

    /// The state or province associated with the placemark.
    @Published public var state: String?

    /// Additional administrative area information for the placemark.
    @Published public var subState: String?

    /// The city associated with the placemark.
    @Published public var city: String?

    /// Additional city-level information for the placemark.
    @Published public var subCity: String?

    /// The street address associated with the placemark.
    @Published public var street: String?

    /// Additional street-level information for the placemark.
    @Published public var subStreet: String?

    /// The geographic region associated with the placemark.
    @Published public var region: CLRegion?

    /// The time zone associated with the placemark.
    @Published public var timeZone: TimeZone?

    /// The name of the inland water body associated with the placemark.
    @Published public var inlandWater: String?

    /// The name of the ocean associated with the placemark.
    @Published public var ocean: String?

    /// The relevant areas of interest associated with the placemark.
    @Published public var areasOfInterest: [String]?

    /// Geocoder
    private let geoCoder = CLGeocoder()

    init () { }

    /// Update to location
    func update (to location: CLLocation) {
        geoCoder.reverseGeocodeLocation(
            location,
            completionHandler: { (placemarks, _) -&amp;gt; Void in
                // We're only using the first place mark.
                if let placeMark = placemarks?[0] {
                    self.placeMark = placeMark
                    self.location = placeMark.location
                    self.name = placeMark.name
                    self.iso = placeMark.isoCountryCode
                    self.country = placeMark.country
                    self.postalCode = placeMark.postalCode
                    self.state = placeMark.administrativeArea
                    self.subState = placeMark.subAdministrativeArea
                    self.city = placeMark.locality
                    self.subCity = placeMark.subLocality
                    self.street = placeMark.thoroughfare
                    self.subStreet = placeMark.subThoroughfare
                    self.region = placeMark.region
                    self.timeZone = placeMark.timeZone
                    self.inlandWater = placeMark.inlandWater
                    self.ocean = placeMark.ocean
                    self.areasOfInterest = placeMark.areasOfInterest

                    // Send a notification that our `@Published` values have been changed.
                    self.objectWillChange.send()
                }
            }
        )
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Usage&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;geocoder.update(to: .init(
    latitude: annotation.coordinate.latitude,
    longitude: annotation.coordinate.longitude
))&lt;/code&gt;&lt;/pre&gt;</description>
  </item>
  <item>
    <title>New Website</title>    <link>https://wesleydegroot.nl/blog/new-website</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/new-website</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:35 +0200</pubDate>
    <category>Website</category>
    <description>&lt;h1&gt;New Website!&lt;/h1&gt;
&lt;p&gt;Welcome to the new website! We hope you like it.&lt;br&gt;
We've been working hard to make it as easy to use as possible.&lt;br&gt;
If you have any feedback, please let me know.&lt;/p&gt;
&lt;h1&gt;New Features&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;New design&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Easier to use&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;More content&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Features&lt;/h1&gt;
&lt;p&gt;This website is built with PHP.&lt;br&gt;
The &lt;code&gt;blog&lt;/code&gt; posts are written in Markdown, and parsed with a custom parser.&lt;br&gt;
It supports Github flavoured markdown.&lt;/p&gt;
&lt;h1&gt;Test&lt;/h1&gt;
&lt;p&gt;This is a inline code test &lt;code&gt;echo "Hello World";&lt;/code&gt;&lt;br&gt;
This is a block code test&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-php"&gt;echo "Hello World";&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a indentation code test&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;echo "Hello World";&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a quote test&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hello World&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is a link test &lt;a href="https://www.google.com"&gt;Google&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is a image test &lt;img src="https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png" alt="Google" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;This is a list test&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Item 1&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Item 2&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Item 3&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is a numbered list test&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Item 1&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Item 2&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Item 3&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is a table test&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Header 1&lt;/th&gt;
&lt;th&gt;Header 2&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cell 1&lt;/td&gt;
&lt;td&gt;Cell 2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cell 3&lt;/td&gt;
&lt;td&gt;Cell 4&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;This is a bold test &lt;strong&gt;Hello World&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is a italic test &lt;em&gt;Hello World&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This is a strikethrough test &lt;del&gt;Hello World&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;This is a underline test &lt;u&gt;Hello World&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;This is a highlight test &lt;mark&gt;Hello World&lt;/mark&gt;&lt;/p&gt;
&lt;p&gt;This is a superscript test &lt;sup&gt;Hello World&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;This is a subscript test &lt;sub&gt;Hello World&lt;/sub&gt;&lt;/p&gt;</description>
  </item>
  <item>
    <title>Snippet: @EnvironmentVariable</title>    <link>https://wesleydegroot.nl/blog/@environmentvariable</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/@environmentvariable</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:36 +0200</pubDate>
    <category>Swift</category>
    <category>Observable</category>
    <category>MacOS</category>
    <category>EnvironmentVariable</category>
    <category>propertyWrapper</category>
    <description>&lt;p&gt;If you are creating commandline apps you sometimes need to acces the operating system environment variables, while you can use &lt;code&gt;getenv(name)&lt;/code&gt; it can come in handy to use a property wrapper if you need to access the environment variables often, or if you are updating them.&lt;/p&gt;
&lt;p&gt;Full code:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;/// A  property wrapper to set and get system's environment variables values.
///
/// ```
/// @EnvironmentVariable(name: "PATH")
/// var path: String?
/// ```
@propertyWrapper
public struct EnvironmentVariable {
    var name: String

    public var wrappedValue: String? {
        get {
            guard let pointer = getenv(name) else { return nil }
            return String(cString: pointer)
        }
        set {
            guard let value = newValue else {
                unsetenv(name)
                return
            }

            setenv(name, value, 1)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;in the future i'll post more about &lt;a href="https://wesleydegroot.nl/blog/tag/propertyWrapper"&gt;@propertyWrapper&lt;/a&gt;s.&lt;/p&gt;</description>
  </item>
  <item>
    <title>OTP Code Generation with CryptoKit: A Swift Approach</title>    <link>https://wesleydegroot.nl/blog/cryptokit-totp</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/cryptokit-totp</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:36 +0200</pubDate>
    <category>Swift</category>
    <category>CryptoKit</category>
    <category>TOTP</category>
    <description>&lt;h2&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;In the realm of secure authentication, &lt;strong&gt;One-Time Passwords (OTPs)&lt;/strong&gt; play a crucial role. Whether it's two-factor authentication (2FA) or protecting sensitive transactions, OTPs provide an additional layer of security. In this article, we'll explore how to generate OTPs using &lt;strong&gt;CryptoKit&lt;/strong&gt;, Apple's powerful cryptographic framework.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;What Is an OTP?&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;An OTP is a temporary password that is valid for a single use or a short period. It ensures that even if an attacker intercepts the password, they won't be able to reuse it. OTPs are commonly used in scenarios like logging into online accounts, confirming transactions, or accessing secure systems.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Understanding TOTP (Time-Based OTP)&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;TOTP&lt;/strong&gt; is a type of OTP that changes over time. It's based on a shared secret key and the current time.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The secret key is known only to the user and the server.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The server and the user's device both calculate the same OTP based on the secret key and the current time.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The OTP is typically a 6 or 8-digit numeric code.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&lt;strong&gt;Generating TOTP with CryptoKit&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Let's dive into the Swift code for generating a TOTP using &lt;strong&gt;CryptoKit&lt;/strong&gt;. We'll assume you already have a shared secret key (usually provided during user registration).&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import CryptoKit
import CommonCrypto
import Foundation

func cryptoKitTOTP(secret: String) -&amp;gt; String {
    let period = TimeInterval(30)
    let digits = 6
    let secret = base32Decode(value: secret)!
    var counter = UInt64(Date().timeIntervalSince1970 / period).bigEndian

    // Generate the key based on the counter.
    let key = SymmetricKey(data: Data(bytes: &amp;amp;counter, count: MemoryLayout.size(ofValue: counter)))
    let hash = HMAC&amp;lt;Insecure.SHA1&amp;gt;.authenticationCode(for: secret, using: key)

    var truncatedHash = hash.withUnsafeBytes { ptr -&amp;gt; UInt32 in
        let offset = ptr[hash.byteCount - 1] &amp;amp; 0x0f

        let truncatedHashPtr = ptr.baseAddress! + Int(offset)
        return truncatedHashPtr.bindMemory(to: UInt32.self, capacity: 1).pointee
    }

    truncatedHash = UInt32(bigEndian: truncatedHash)
    truncatedHash = truncatedHash &amp;amp; 0x7FFF_FFFF
    truncatedHash = truncatedHash % UInt32(pow(10, Float(digits)))

    return String(format: "%0*u", digits, truncatedHash)
}

print(cryptoKitTOTP(secret: "5FAA5JZ7WHO5WDNN"))&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;By leveraging &lt;strong&gt;CryptoKit&lt;/strong&gt;, you can easily implement TOTP generation in your iOS apps. Remember to securely store and manage the shared secret key. OTPs enhance security and protect your users' accounts from unauthorized access.&lt;/p&gt;
&lt;p&gt;For more details, refer to the &lt;a href="https://developer.apple.com/documentation/cryptokit"&gt;official Apple CryptoKit documentation&lt;/a&gt;.&lt;/p&gt;</description>
  </item>
  <item>
    <title>Implementing Admob in SwiftUI</title>    <link>https://wesleydegroot.nl/blog/admob-in-swiftui</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/admob-in-swiftui</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:37 +0200</pubDate>
    <category>Swift</category>
    <category>Admob</category>
    <description>&lt;p&gt;Recently i was working on a &lt;a href="https://wesleydegroot.nl/apps/ddv"&gt;project&lt;/a&gt; that required me to implement Admob in a SwiftUI app.&lt;/p&gt;
&lt;p&gt;I encountered some issues while implementing Admob in SwiftUI, so i decided to make a library &lt;a href="https://github.com/0xWDG/Admob-SwiftUI"&gt;Admob-SwiftUI&lt;/a&gt; that makes it easy to implement Admob in SwiftUI.&lt;/p&gt;
&lt;h3&gt;Which problems did i encounter?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Admob banner not showing up.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Padding for the Admob banner.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The new EU consent policy &lt;code&gt;UMPConsentInformation&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Unable to update the consent status.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Ad area unusable if ad is not shown.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Admob Banner not showing up&lt;/h3&gt;
&lt;p&gt;The Admob banner was not showing up because i had not set the &lt;a href="https://developers.google.com/admob/ios/privacy/api/reference/Classes/UMPConsentInformation"&gt;UMPConsentInformation&lt;/a&gt; correctly, see the &lt;a href="https://support.google.com/admob/answer/13554116"&gt;Documentation&lt;/a&gt;, and the page in &lt;a href="https://apps.admob.com/v2/privacymessaging/gdpr"&gt;Admob Console&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;What did i try to fix the padding?&lt;/h4&gt;
&lt;p&gt;First i added in every view a padding, (on the bottom) to make sure that the ad would not overlap the view below it.&lt;br&gt;
Using &lt;code&gt;.padding(.bottom, consent.haveConsent ? 50 : 0)&lt;/code&gt;.&lt;br&gt;
This worked okay, but created a white/black bar where the ad would be shown, and i needed to add it in every view what i displayed on screen, this increases the chance of forgetting to add it.&lt;/p&gt;
&lt;p&gt;Using a padding on each view in the TabView. (this works okay, but creates a white/black bar where the ad will be shown) &lt;code&gt;.padding(.bottom, consent.haveConsent ? 50 : 0)&lt;/code&gt;.&lt;br&gt;
This is also not the solution i wanted.&lt;/p&gt;
&lt;p&gt;I searched if there is a way to add some extra padding to the contents of the views in the &lt;code&gt;TabView&lt;/code&gt;, after a lot of searching i found &lt;a href="https://developer.apple.com/documentation/swiftui/view/contentmargins(_:for:)?changes=_4"&gt;contentMargins(_:for:)&lt;/a&gt; and this looked promising, so i tried to add that to my app.&lt;br&gt;
But unfortunalet there was a catch, it only works on iOS 17+ and my app supports iOS 16 and up.&lt;br&gt;
I tried to use it with a &lt;code&gt;if #available(iOS 17, *)&lt;/code&gt;, but the issue is, you can't use &lt;code&gt;if #available(iOS 17, *)&lt;/code&gt; in a &lt;code&gt;View&lt;/code&gt; in SwiftUI.&lt;/p&gt;
&lt;p&gt;So i needed to come up with a different solution...&lt;/p&gt;
&lt;p&gt;What about a &lt;code&gt;ViewModifier&lt;/code&gt;? no that would not work...&lt;/p&gt;
&lt;p&gt;What about a &lt;code&gt;ViewBuilder&lt;/code&gt;?&lt;br&gt;
This can work, let's try it out.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;extension View {
    @ViewBuilder
    func addAdPadding(height: CGFloat) -&amp;gt; some View {
        if #available(iOS 17.0, *) {
            // If iOS 17+ use contentMargins
            // This is the nices solution, 
            // but it only works on iOS 17+
            self.contentMargins(
                .bottom,
                height + 10,
                for: .scrollContent
            )
        } else {
            // Fallback on earlier versions
            self.padding(
                .bottom,
                height
            )
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We have our final solution, this works on iOS 16 and up, and it's easy to use.&lt;/p&gt;
&lt;h3&gt;The new EU consent policy &lt;code&gt;UMPConsentInformation&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;You create a view with the class name &lt;code&gt;AdConsentView&lt;/code&gt; and then you can call the &lt;code&gt;updateConsent&lt;/code&gt; function to update the consent status.&lt;/p&gt;
&lt;p&gt;I call it like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct AdConsentView: View {
    @EnvironmentObject
    private var adHelper: AdHelper

    var body: some View {
        VStack { }
        .background(formViewControllerRepresentableView)
        .onAppear {
            guard !hasViewAppeared else { return }
            hasViewAppeared = true

            DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                self.askConsent()
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Full code: &lt;a href="https://github.com/0xWDG/Admob-SwiftUI/blob/main/Sources/Admob-SwiftUI/AdConsentView.swift"&gt;https://github.com/0xWDG/Admob-SwiftUI/blob/main/Sources/Admob-SwiftUI/AdConsentView.swift&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Updating the consent status&lt;/h3&gt;
&lt;p&gt;The code for updating the consent is:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;func updateConsent() {
    GoogleMobileAdsConsentManager.shared.presentPrivacyOptionsForm(
        from: formViewControllerRepresentable.viewController
    ) { (formError) in
        guard let formError else { return }

        // Handle the error
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The problem with &lt;code&gt;AdConsentView&lt;/code&gt; is that your SwiftUI view is not re-usable for calling only the &lt;code&gt;updateConsent&lt;/code&gt; function, so you have to create &lt;code&gt;formViewControllerRepresentable.viewController&lt;/code&gt; and the &lt;code&gt;updateConsent&lt;/code&gt; function in multiple files, in my case this did not work and i was unable to update the consent of the user.&lt;/p&gt;
&lt;p&gt;My solution was to point to the &lt;code&gt;updateConsent()&lt;/code&gt; function, to that i can call the &lt;code&gt;updateConsent&lt;/code&gt; function from the &lt;code&gt;AdHelper&lt;/code&gt; class.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct AdConsentView: View {
    func updateConsent() {
        GoogleMobileAdsConsentManager.shared.presentPrivacyOptionsForm(
            from: formViewControllerRepresentable.viewController
        ) { (formError) in
            guard let formError else { return }

            // Handle the error
        }
    }

    @MainActor
    func askConsent() {
        // Point to the updateConsent function
        adHelper.updateConsent = updateConsent 

        // initial consent question
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Ad area unusable if ad is not shown&lt;/h3&gt;
&lt;p&gt;If the ad is not shown, the view (zstack) will be overlap the view below it, making the view below it unusable, this is intentional if the ad is shown, but if the ad is not shown, the view below it should be usable.&lt;/p&gt;
&lt;p&gt;the way to fix this is to set the opacity of the ad to 0 if the ad is not shown. like the snippet below.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.opacity(adHelper.showingAd ? 1 : 0)&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;Reset the consent status&lt;/h3&gt;
&lt;p&gt;You can use &lt;code&gt;UMPConsentInformation.sharedInstance.reset()&lt;/code&gt; to reset the consent status.&lt;br&gt;
Since the library is already handling the consent status, you can use&lt;br&gt;
&lt;code&gt;adHelper.resetConsent()&lt;/code&gt; (which is shorter and easier to remember)&lt;/p&gt;
&lt;h2&gt;Before the library&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct ContentView: View {
    @ObservedObject var adHelper = AdHelper(
        adUnitId: "ca-app-pub-5555094756467155/7714412839"
    )

    ZStack {
            TabView {
                Text("First View")
                    .tabItem {
                        Image(systemName: "1.square.fill")
                        Text("First")
                    }
                    .padding(.bottom, adHelper.adHeight)
                UpdateConsent()
                    .tabItem {
                        Image(systemName: "2.square.fill")
                        Text("Second")
                    }
                    .padding(.bottom, adHelper.adHeight)
            }

            AdConsentView()
                .environmentObject(adHelper)

            if adHelper.haveConsent {
                VStack {
                    Spacer()
                    BannerView(adUnitID: adHelper.adUnitId)
                        .background(.clear)
                        .frame(width: 320, height: adHelper.adHeight)
                        .padding(.bottom, adHelper.adHeight + 1)
                        .opacity(adHelper.showingAd ? 1 : 0)
                        .environmentObject(adHelper)
                }
            }
    }
    .environmentObject(adHelper)
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;With the library&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct ContentView: View {
    @ObservedObject var adHelper = AdHelper(
        adUnitId: "ca-app-pub-5555094756467155/7714412839"
    )

    AdView {
            TabView {
                Text("First View")
                    .tabItem {
                        Image(systemName: "1.square.fill")
                        Text("First")
                    }
                UpdateConsent()
                    .tabItem {
                        Image(systemName: "2.square.fill")
                        Text("Second")
                    }
            }
    }
    .environmentObject(adHelper)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Update/Reset the consent status&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct UpdateConsent: View {
    @EnvironmentObject
    private var adHelper: AdHelper

    var body: some View {
        ScrollView {
            VStack {
                Button("Reset consent", role: .destructive) {
                    adHelper.resetConsent()
                }

                Button("Update Consent") {
                    adHelper.updateConsent()
                }
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The library is available on Github: &lt;a href="https://github.com/0xWDG/Admob-SwiftUI"&gt;https://github.com/0xWDG/Admob-SwiftUI&lt;/a&gt;.&lt;/p&gt;</description>
  </item>
  <item>
    <title>Xcode shortcuts</title>    <link>https://wesleydegroot.nl/blog/xcode-shortcuts</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/xcode-shortcuts</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:38 +0200</pubDate>
    <category>Xcode</category>
    <category>shortcuts</category>
    <description>&lt;h2&gt;&lt;strong&gt;Mastering Xcode: Boost Your Productivity with Essential Shortcuts&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;As developers, we're always on the quest for efficiency. Whether you're a seasoned Xcode user or just starting out, harnessing the power of keyboard shortcuts can significantly enhance your coding experience. In this article, we'll dive into the world of Xcode shortcuts, uncovering hidden gems that will streamline your workflow and make you feel like a coding wizard.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Why Xcode Shortcuts Matter&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Imagine this: You're knee-deep in your Swift code, tweaking UI elements, debugging, and navigating through project files. Instead of reaching for your mouse every time, what if you could perform these tasks effortlessly with a few keystrokes? That's where Xcode shortcuts come to the rescue.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Navigating the Xcode Landscape&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Before we delve into the shortcuts themselves, let's orient ourselves within Xcode's interface. Remember these three key areas:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Navigator&lt;/strong&gt; &lt;code&gt;⌘ + 0&lt;/code&gt;: On the left, the Navigator provides access to project files, source control, and more.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Inspectors&lt;/strong&gt; &lt;code&gt;⌘ + ⌥ + 0&lt;/code&gt;: To the right, Inspectors reveal contextual information about your code.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Debug Area&lt;/strong&gt; &lt;code&gt;⇧ + ⌘ + Y&lt;/code&gt;: At the bottom, the Debug Area displays logs, breakpoints, and variables during debugging.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;&lt;strong&gt;Essential Xcode Shortcuts&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Now, let's explore some of my favorite productivity shortcuts:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Jump to Definition&lt;/strong&gt; &lt;code&gt;⌃ + ⌘ + J&lt;/code&gt;: Instantly navigate to the definition of a class, function, or variable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Quick Open File&lt;/strong&gt; &lt;code&gt;⌘ + Shift + O&lt;/code&gt;: Find and open any file in your project swiftly.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Refactor&lt;/strong&gt; &lt;code&gt;⌃ + ⌘ + R&lt;/code&gt;: Rename variables, methods, or classes seamlessly.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Comment/Uncomment&lt;/strong&gt; &lt;code&gt;⌘ + /&lt;/code&gt;: Add or remove comments with a single keystroke.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Build and Run&lt;/strong&gt; &lt;code&gt;⌘ + R&lt;/code&gt;: Compile and launch your app without leaving the keyboard.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Summary / list&lt;/h2&gt;
&lt;table&gt;
  &lt;tr&gt;
    &lt;td&gt;Build&lt;/td&gt;
    &lt;td&gt;&lt;code&gt;⌘ + B&lt;/code&gt;&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Run&lt;/td&gt;
    &lt;td&gt;&lt;code&gt;⌘ + R&lt;/code&gt;&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Test&lt;/td&gt;
    &lt;td&gt;&lt;code&gt;⌘ + U&lt;/code&gt;&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Build&lt;/td&gt;
    &lt;td&gt;&lt;code&gt;⌘ + B&lt;/code&gt;&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Stop&lt;/td&gt;
    &lt;td&gt;&lt;code&gt;⌘ + .&lt;/code&gt;&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Clean&lt;/td&gt;
    &lt;td&gt;&lt;code&gt;⌘ + ⇧ + K&lt;/code&gt;&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Clean the build folder&lt;/td&gt;
    &lt;td&gt;&lt;code&gt;⌘ + ⇧ + ⌥ + K&lt;/code&gt;&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Open quickly&lt;/td&gt;
    &lt;td&gt;&lt;code&gt;⇧ + ⌘ + O&lt;/code&gt;&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Code completion&lt;/td&gt;
    &lt;td&gt;&lt;code&gt;⌃ + Space&lt;/code&gt;&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;&lt;em&gt;References&lt;/em&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/library/archive/documentation/IDEs/Conceptual/xcode_help-command_shortcuts/Introduction/Introduction.html"&gt;Apple Developer Documentation: Xcode Keyboard Shortcuts and Gestures&lt;/a&gt; ¹&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://peterfriese.dev/posts/xcode-shortcuts/"&gt;Peter Friese: Essential Xcode Shortcuts for More Efficient Coding&lt;/a&gt; ²&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://betterprogramming.pub/13-xcode-shortcuts-to-boost-your-productivity-329c90512309"&gt;Better Programming: 13 Xcode Shortcuts to Boost Your Productivity&lt;/a&gt; ³&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;em&gt;Fun fact:&lt;/em&gt; this article is partly generated using bing Copilot!&lt;/p&gt;</description>
  </item>
  <item>
    <title>SwiftUI property wrappers</title>    <link>https://wesleydegroot.nl/blog/swiftui-property-wrappers</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/swiftui-property-wrappers</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:38 +0200</pubDate>
    <category>SwiftUI</category>
    <category>propertyWrapper</category>
    <description>&lt;p&gt;Let's dive into the fascinating world of &lt;strong&gt;SwiftUI property wrappers&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;These powerful constructs allow you to manage data, state, and environment information in your SwiftUI views. 🚀&lt;/p&gt;
&lt;h2&gt;Understanding SwiftUI Property Wrappers&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;State&lt;/strong&gt;: Things the view creates (not owns, because Views don't own anything…).&lt;br&gt;
For example: &lt;code&gt;@State&lt;/code&gt;, &lt;code&gt;@StateObject&lt;/code&gt;, &lt;code&gt;@AppStorage&lt;/code&gt;…&lt;br&gt;
Can also more generally mean “the state of the app”, which beyond the view’s state, would also include the view’s dependencies, as well as other parts of the app’s state that don’t affect the view. The meaning of the term through the document usually means the former definition (ie, things the view creates), but can change based on the context.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Dependencies&lt;/strong&gt;: Things the view receives and doesn’t create.&lt;br&gt;
For example: &lt;code&gt;@Binding&lt;/code&gt;, &lt;code&gt;let&lt;/code&gt;, &lt;code&gt;@Environment&lt;/code&gt;, &lt;code&gt;@EnvironmentObject&lt;/code&gt;, &lt;code&gt;@ObservedObject&lt;/code&gt;…&lt;br&gt;
Also anything passed to a custom init if there is one.&lt;/p&gt;
&lt;p&gt;Property wrappers are a fundamental part of SwiftUI, helping reduce boilerplate code and enabling seamless data flow between views. Let's explore some of the most commonly used property wrappers:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;@State&lt;/code&gt;&lt;/strong&gt;: This property wrapper allows you to manage small amounts of &lt;strong&gt;value type data&lt;/strong&gt; locally within a view. When the state changes, SwiftUI automatically updates the view. Use it for UI-related state, such as toggles, counters, or text input fields.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;@Binding&lt;/code&gt;&lt;/strong&gt;: When you need to share data between different views, &lt;code&gt;@Binding&lt;/code&gt; comes to the rescue. It refers to &lt;strong&gt;value type data owned by another view&lt;/strong&gt;. Changes made locally to the binding propagate back to the original data source. Think of it as a two-way communication channel.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;@ObservedObject&lt;/code&gt;&lt;/strong&gt;: Use this property wrapper to refer to an instance of an external class that conforms to the &lt;code&gt;ObservableObject&lt;/code&gt; protocol. It's perfect for managing &lt;strong&gt;reference type data&lt;/strong&gt; (e.g., network requests, Core Data objects) that doesn't belong to the view itself.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;@Published&lt;/code&gt;&lt;/strong&gt;: Attached to properties inside an &lt;code&gt;ObservableObject&lt;/code&gt;, &lt;code&gt;@Published&lt;/code&gt; tells SwiftUI to refresh any views that use this property when it changes. It's like a signal to update the UI when the underlying data updates.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;@Environment&lt;/code&gt;&lt;/strong&gt;: This wrapper lets you read data from the system environment, such as color schemes, accessibility options, and trait collections. You can also add your own custom keys to the environment. It doesn't own its data but provides access to system-level information.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;@AppStorage&lt;/code&gt;&lt;/strong&gt;: If you need to read and write values from &lt;code&gt;UserDefaults&lt;/code&gt;, &lt;code&gt;@AppStorage&lt;/code&gt; is your friend. It owns its data and simplifies persistent storage for simple values like user preferences or settings.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;@FetchRequest&lt;/code&gt;&lt;/strong&gt;: When working with Core Data, use &lt;code&gt;@FetchRequest&lt;/code&gt; to start a fetch request for a specific entity. It owns its data and provides a convenient way to retrieve managed objects from your data store.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;@ScaledMetric&lt;/code&gt;&lt;/strong&gt;: This wrapper reads the user's Dynamic Type setting and scales numbers based on an original value you provide. It's useful for adapting font sizes and other metrics to the user's preferences.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Custom Property Wrappers&lt;/h2&gt;
&lt;p&gt;You can also create your own custom property wrappers to encapsulate specific behavior or data management logic. By defining a property wrapper type, you can reuse it across your codebase and ensure consistent behavior wherever it's used.&lt;/p&gt;
&lt;p&gt;Here's a simple example of a custom property wrapper that logs changes to a property:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;@propertyWrapper
struct LogChanges&amp;lt;Value&amp;gt; {
    var wrappedValue: Value {
        didSet {
            print("Value changed to \(wrappedValue)")
        }
    }

    init(wrappedValue: Value) {
        self.wrappedValue = wrappedValue
    }
}

struct ContentView: View {
    @LogChanges
    var count = 0

    var body: some View {
        VStack {
            Button("Increment") {
                count += 1
            }
            Text("Count: \(count)")
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, the &lt;code&gt;LogChanges&lt;/code&gt; property wrapper prints a message whenever the &lt;code&gt;count&lt;/code&gt; property changes. You can create custom property wrappers to add additional functionality or constraints to your properties.&lt;/p&gt;
&lt;h3&gt;Example 2: Mimicing @Published&lt;/h3&gt;
&lt;p&gt;Here's another example that mimics the behavior of &lt;code&gt;@Published&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;@propertyWrapper
struct Store&amp;lt;Value&amp;gt; :DynamicProperty {
    let storage: State&amp;lt;Value&amp;gt;

    init(wrappedValue value: Value) {
        self.storage = State&amp;lt;Value&amp;gt;(initialValue: value)
    }

    public var wrappedValue: Value {
        get { storage.wrappedValue }
        nonmutating set { storage.wrappedValue = newValue }
    }

    public var projectedValue: Binding&amp;lt;Value&amp;gt; {
        storage.projectedValue
    }
}

struct ContentView: View {
    @Store
    var isOn = false

    var body: some View {
        VStack {
            Text(isOn ? "yes" : "Nope")

            Toggle(isOn: $isOn, label: {
                Text("IsOn?")
            })
        }
        .padding()
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;SwiftUI property wrappers are like magic spells that empower your views. By choosing the right one for each situation, you'll create elegant, responsive interfaces. Happy coding! 🌟&lt;/p&gt;</description>
  </item>
  <item>
    <title>iCloud Drive Tips &amp; Tricks</title>    <link>https://wesleydegroot.nl/blog/icloud-drive-tips-and-tricks</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/icloud-drive-tips-and-tricks</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:39 +0200</pubDate>
    <category>iCloud</category>
    <description>&lt;h3&gt;&lt;strong&gt;What Is iCloud Drive?&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;iCloud Drive is Apple's cloud-based file storage and synchronization service. It allows you to store, organize, and collaborate on files and folders across all your Apple devices. Whether you're using a Mac, iPhone, iPad, or even accessing your files via iCloud.com, iCloud Drive ensures seamless synchronization.&lt;br&gt;
iCloud Drive simplifies file management, collaboration, and access across your Apple ecosystem. Whether you're a student, professional, or creative, harness the power of iCloud Drive to keep your digital life organized and connected.&lt;/p&gt;
&lt;h2&gt;Must know commandline commands&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Download ‘all’ files in the current &amp;amp; sub folders.&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;find . -type f -name "*.icloud" -exec brctl download “{}” \;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Download a specific file:&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;brctl download filename&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Remove the local download of a file&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;brctl evict filename&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;View iCloud log:&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;brctl log -w --shorten&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Diagnose errors:&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;brctl diagnose&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;Debugging &amp;amp; more&lt;/h2&gt;
&lt;p&gt;If you are having issues with iCloud Drive, you can use the following commands in terminal to help diagnose the problem.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Check iCloud Progress:&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;brctl monitor com.apple.CloudDocs|grep %&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Check iCloud Download Progress:&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;brctl monitor com.apple.CloudDocs|grep ↓&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Check iCloud Upload Progress:&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;brctl monitor com.apple.CloudDocs|grep ↑&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Check iCloud Errors:&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;brctl log -w --shorten&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Check iCloud Diagnose:&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;brctl diagnose&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Check iCloud Status:&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;brctl status&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;Full list of commands&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;brctl&lt;/code&gt; is the command line tool for iCloud Drive. Here is a full list of commands:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;╭── 👤 wesley 📂 ~/d/w/wesleydegroot.nl/blog/
╰─&amp;gt; brctl     
Usage: brctl &amp;lt;command&amp;gt; [command-options and arguments]

    -h,--help            show this help

COMMANDS

diagnose [options] [--doc|-d &amp;lt;document-path&amp;gt;] [&amp;lt;diagnosis-output-path&amp;gt;]
    diagnose and collect logs

    -M,--collect-mobile-documents[=&amp;lt;container&amp;gt;]  (default: all containers)
    -s,--sysdiagnose     Do not collect what's already part of sysdiagnose
    -t,--uitest          Collect logs for UI tests
    -n,--name=&amp;lt;name&amp;gt;     Change the device name
    -f,--full            Do a full diagnose, including server checks
    -d,--doc=&amp;lt;document-path&amp;gt;
                         Collect additional information about the document at that path.
                         Helps when investigating an issue impacting a specific document.
    -e,--no-reveal       Do not reveal diagnose in the Finder when done
    [&amp;lt;diagnosis-output-path&amp;gt;]
                         Specifies the output path of the diagnosis; -n becomes useless.

log [options] [&amp;lt;command&amp;gt;]

    -a,--all                         Show all system logs
    -p,--predicate                   Additional predicate (see `log help predicates`)
    -x,--process &amp;lt;name&amp;gt;              Filter events from the specified process
    -d,--path=&amp;lt;logs-dir&amp;gt;             Use &amp;lt;logs-dir&amp;gt; instead of default
    --last num [m|h|d]               Limits the captured events to the period starting at the given interval ago from the current time
    -S,--start="YYYY-MM-DD HH:MM:SS" Start log dump from a specified date
    -E,--end="YYYY-MM-DD HH:MM:SS"   Stop log dump after a specified date
    -b                               Show CloudDocs logs
    -f                               Show FileProvider related logs
    -F                               Show FruitBasket related logs
    -N                               Show network related logs (should be used in conjonction with another flag)
    -g                               Show Genstore related logs
    -i                               Show SQL and CloudDocs logs
    -o                               Show local storage logs
    -D                               Show logs from the Denator subsystem
    -z,--local-timezone              Display timestamps within local timezone
    --dark-mode                      Adapt color scheme to dark mode terminal
    -q,--quick                       Print logs without heavy pre-processing

dump [options] [&amp;lt;container&amp;gt;]
    dump the CloudDocs database

    -o,--output=&amp;lt;file-path&amp;gt;
                         redirect output to &amp;lt;file-path&amp;gt;
    -d,--database-path=&amp;lt;db-path&amp;gt;
                         Use the database at &amp;lt;db-path&amp;gt;
    -e,--enterprise
                         Use the Data Separated database
    -i,--itemless
                         Don't dump items from the db
    -u,--upgrade
                         Upgrade the db if necessary before dumping

    [&amp;lt;container&amp;gt;]        the container to be dumped

status [&amp;lt;containers&amp;gt;]
    Prints items which haven't been completely synced up / applied to disk

    [&amp;lt;container&amp;gt;]        the container to be dumped

accounts [options]
    Displays iCloudDrive eligible accounts and their logged in/out status and directory name
    -w,--wait            wait for logged in accounts to load

quota
    Displays the available quota in the account

monitor [options] [&amp;lt;container&amp;gt; ...]
    monitor activity
    -g                   dump global activity of the iCloud Drive
    -i                   dump changes incrementally
    -t                   amount of time in seconds to run the query, the query will stop after the specified time
    -S,--scope=&amp;lt;scope&amp;gt;
                         restrict the NSMetadataQuery scope to docs, data, external or a combination

    [&amp;lt;container&amp;gt; ...]    list of containers to monitor, ignored when -g is used&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Help&lt;/h4&gt;
&lt;p&gt;Something isn't working!&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;Finder freezes&lt;/summary&gt;
Sometimes this can happen, especially when you have a lot of files in your iCloud Drive. You can try to find the file causing Finder to freeze by using the following command &lt;code&gt;brctl monitor com.apple.CloudDocs|grep % &lt;/code&gt;, this will show you the progress of the files being downloaded. If you see a file that is stuck for a long time, you can try to download it manually by using the following command &lt;code&gt;brctl download filename&lt;/code&gt;. If that doesn't work, you can try to remove the local download of the file by using the following command &lt;code&gt;brctl evict filename&lt;/code&gt;. This will remove the local download of the file and try to download it again. If that doesn't work, you can try to diagnose the issue by using the following command &lt;code&gt;brctl diagnose&lt;/code&gt;. This will collect logs and diagnose the issue. If you still can't find the issue, you can try to contact Apple Support for further assistance.&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;I unzipped a masively big zip and now the contents of the folder are not shown anymore (e.g. on the Desktop)&lt;/summary&gt;
Go to &lt;a href='https://www.icloud.com/iclouddrive'&gt;https://www.icloud.com/iclouddrive&lt;/a&gt; and delete the files.
&lt;/details&gt;
&lt;h4&gt;Tips&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Do not use a lot of non-standard characters in your file names. This can cause issues with iCloud Drive.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Do not end your file names with a space. This can cause issues with iCloud Drive.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Do not save development folders like &lt;code&gt;node_modules&lt;/code&gt; or &lt;code&gt;.git&lt;/code&gt; in your iCloud Drive. This can take up a lot of space and cause issues with iCloud Drive.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Do not use iCloud Drive as a backup solution. It's not designed for that and you can lose your files if you're not careful.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;i love iCloud Drive and it's features. It's a great way to keep all your files in sync and available on all your devices.&lt;br&gt;
Unfortunately, it's not always working as expected. But with the above commands you can diagnose and fix most of the issues.&lt;br&gt;
I hope this article helps you with your iCloud Drive issues. If you have any questions, feel free to ask them in the comments below.&lt;/p&gt;</description>
  </item>
  <item>
    <title>async/await</title>    <link>https://wesleydegroot.nl/blog/async-await</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/async-await</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:39 +0200</pubDate>
    <category>async</category>
    <category>await</category>
    <category>Swift</category>
    <description>&lt;p&gt;Let's delve into the fascinating world of &lt;strong&gt;Swift's &lt;code&gt;async/await&lt;/code&gt;&lt;/strong&gt;.&lt;br&gt;
This powerful feature, introduced in Swift 5.5, revolutionizes asynchronous programming, making it more intuitive and readable. Buckle up as we explore the ins and outs of &lt;code&gt;async/await&lt;/code&gt;!&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;async/await&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;At its core, &lt;code&gt;async/await&lt;/code&gt; simplifies handling asynchronous tasks. It allows you to write asynchronous code that looks almost synchronous. Here's how it works:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;async&lt;/code&gt; Functions: You mark a function as &lt;code&gt;async&lt;/code&gt;. Inside this function, you can use &lt;code&gt;await&lt;/code&gt; to pause execution until an asynchronous operation completes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;await&lt;/code&gt; Expression: When you encounter an &lt;code&gt;await&lt;/code&gt; expression, the function suspends execution until the awaited task finishes. Meanwhile, other tasks can continue running concurrently.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Example 1: Fetching Data&lt;/h2&gt;
&lt;p&gt;Let's start with a common scenario: fetching data from a remote API. Imagine we have a callback-based function for fetching data:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;func fetchData(completion: @escaping (Data?, Error?) -&amp;gt; Void) {
    let url = URL(string: "https://api.example.com/data")!
    URLSession.shared.dataTask(with: url) { data, _, error in
        completion(data, error)
    }.resume()
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, let's convert this to an &lt;code&gt;async&lt;/code&gt; function using &lt;code&gt;async/await&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;func fetchData() async throws -&amp;gt; Data {
    let url = URL(string: "https://api.example.com/data")!
    return try await URLSession.shared.data(from: url).0
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the &lt;code&gt;async&lt;/code&gt; version:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;We use &lt;code&gt;try await&lt;/code&gt; to wait for the data to be fetched.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The error handling is cleaner, thanks to Swift's &lt;code&gt;throws&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Example 2: Multiple API Calls&lt;/h2&gt;
&lt;p&gt;Imagine we have a callback-based function for fetching data:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;func fetchUser(id: Int, completion: @escaping (User?, Error?) -&amp;gt; Void) {
    // Fetch user details...
}

func fetchPosts(for user: User, completion: @escaping ([Post]?, Error?) -&amp;gt; Void) {
    // Fetch posts for the user...
}

// Usage:

fetchUser(id: 1) { user, error in
  guard let user = user else {
    print(error)
  }
  // Guard for no error
  self.fetchPosts(for: user) { posts, error in
    // Process user and posts
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Suppose we need to fetch a user and then fetch their posts. In the callback-based world, this can get messy. But with &lt;code&gt;async/await&lt;/code&gt;, it's elegant:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;func fetchUser(id: Int) async throws -&amp;gt; User {
    // Fetch user details...
}

func fetchPosts(for user: User) async throws -&amp;gt; [Post] {
    // Fetch posts for the user...
}

// Usage
Task {
    do {
        let user = try await fetchUser(id: 1)
        let posts = try await fetchPosts(for: user)
        // Process user and posts
    } catch {
        // Handle errors
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Running multiple tasks concurrently&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;func fetchUser(id: Int) async -&amp;gt; User {
    // Fetch user details...
}

func fetchPosts(for userID: Int) async -&amp;gt; [Post] {
    // Fetch posts for the user...
}

// Usage
import Foundation

Task {
    async let user = fetchUser(id: 1)
    async let userPosts = fetchPosts(for: 1)

    await updateUI(user: user, userPosts: userPosts)
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Wait/Sleep in a Task&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;Task {
    // Delay the task by 1 second:
    try await Task.sleep(nanoseconds: 1_000_000_000)

    // Perform our operation
    // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Cancellation&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;func fetchUser(id: Int) async -&amp;gt; User {
    // Fetch user details...
}

func fetchPosts(for userID: Int) async -&amp;gt; [Post] {
    // Fetch posts for the user...
}

// Usage
import Foundation

let task = Task {
    async let user = fetchUser(id: 1)
    async let userPosts = fetchPosts(for: 1)

    await updateUI(user: user, userPosts: userPosts)
}

// Cancel the task
task.cancel()&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Difference between &lt;code&gt;Task&lt;/code&gt; and &lt;code&gt;Task.detached&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Task&lt;/code&gt; is a structured concurrency task that runs within the scope of the current task.&lt;br&gt;
It is automatically cancelled when the parent task is cancelled.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Task.detached&lt;/code&gt; is a detached task that runs independently of the current task.&lt;br&gt;
It is not automatically cancelled when the parent task is cancelled.&lt;/p&gt;
&lt;h2&gt;Ensure that your code is (not)running on the main thread&lt;/h2&gt;
&lt;p&gt;Explicitly running on the main thread&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;Task { @MainActor in 

}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Doing heavy work (background) and then updating the UI (main thread/@MainActor)&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;Task.detached(priority: .userInitiated) {
    await someHeavyBackgroundOperation()
    await MainActor.run {
        // Perform UI updates
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Doing heavy work on the background using &lt;code&gt;nonisolated&lt;/code&gt; and &lt;code&gt;async&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;private nonisolated func doHeavyWork() async -&amp;gt; Bool {
  sleep(10)
  return true
}

Task {
    let result = await doHeavyWork()
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Limitations&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;async/await&lt;/code&gt; is available starting from Swift 5.5.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;async/await&lt;/code&gt; is not available on all platforms. It is available on iOS 15, macOS 12, watchOS 8, and tvOS 15.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;async/await&lt;/code&gt; is not available in Objective-C code.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;By embracing &lt;code&gt;async/await&lt;/code&gt;, you create a better development experience and write more efficient code. Say goodbye to callback hell and welcome a cleaner, more expressive way of handling asynchronous tasks in Swift. 🚀&lt;/p&gt;
&lt;p&gt;Resources:&lt;br&gt;
&lt;a href="https://docs.swift.org/swift-book/LanguageGuide/Concurrency.html#async-await"&gt;https://docs.swift.org/swift-book/LanguageGuide/Concurrency.html#async-await&lt;/a&gt;&lt;br&gt;
&lt;a href="https://developer.apple.com/documentation/xcode/improving-app-responsiveness"&gt;https://developer.apple.com/documentation/xcode/improving-app-responsiveness&lt;/a&gt;&lt;/p&gt;</description>
  </item>
  <item>
    <title>@dynamicMemberLookup</title>    <link>https://wesleydegroot.nl/blog/@dynamicmemberlookup</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/@dynamicmemberlookup</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:40 +0200</pubDate>
    <category>dynamicMemberLookup</category>
    <category>Swift</category>
    <description>&lt;p&gt;Let's delve into the fascinating world of Swift's &lt;code&gt;@dynamicMemberLookup&lt;/code&gt;. This attribute, introduced in Swift 4.2, provides a powerful mechanism for accessing properties and methods dynamically even those that aren't known until runtime.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;@dynamicMemberLookup&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;At its core, &lt;code&gt;@dynamicMemberLookup&lt;/code&gt; instructs Swift to call a &lt;strong&gt;subscript method&lt;/strong&gt; when accessing properties.&lt;br&gt;
This method, called &lt;code&gt;subscript(dynamicMember:)&lt;/code&gt;, is required. Here's how it works:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Attribute Definition&lt;/strong&gt;: You mark a type (such as a struct or class) with &lt;code&gt;@dynamicMemberLookup&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Subscript Method&lt;/strong&gt;: You implement the &lt;code&gt;subscript(dynamicMember:)&lt;/code&gt; method within that type. This method takes a string parameter representing the name of the requested property and returns any value you like.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Use Case: Dynamic Properties&lt;/h2&gt;
&lt;p&gt;Let's make a simple example. Imagine we have a &lt;code&gt;Person&lt;/code&gt; struct that reads its values from a dictionary:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;@dynamicMemberLookup
struct Person {
    private let properties = [
        "name": "Wesley de Groot",
        "city": "Haarlem"
    ]

    subscript(dynamicMember member: String) -&amp;gt; String {
        return properties[member, default: ""]
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this case:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The &lt;code&gt;@dynamicMemberLookup&lt;/code&gt; attribute ensures that our type handles dynamic member lookup.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The &lt;code&gt;subscript(dynamicMember:)&lt;/code&gt; method looks up the member name in the dictionary and returns its value.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now, we can access properties that don't actually exist as direct properties on the &lt;code&gt;Person&lt;/code&gt; type:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;let wesley = Person()
print(wesley.name) // Prints "Wesley de Groot"
print(wesley.city) // Prints "Haarlem"
print(wesley.pet) // Prints an empty string&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Even though &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;city&lt;/code&gt;, and &lt;code&gt;pet&lt;/code&gt; are not actual properties, they are looked up at runtime.&lt;br&gt;
This feature bridges the gap between Swift's strong type safety and the more relaxed behavior of dynamic languages like PHP and JavaScript.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;@dynamicMemberLookup&lt;/code&gt; is a powerfull attribute to create easier to read code, it can be used on arrays and therefore also for &lt;a href="https://github.com/saoudrizwan/DynamicJSON"&gt;JSON&lt;/a&gt;!&lt;br&gt;
&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Resources: &lt;a href="https://docs.swift.org/swift-book/documentation/the-swift-programming-language/attributes#dynamicMemberLookup"&gt;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/attributes#dynamicMemberLookup&lt;/a&gt;&lt;/p&gt;</description>
  </item>
  <item>
    <title>SimpleNetworking</title>    <link>https://wesleydegroot.nl/blog/simplenetworking</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/simplenetworking</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:40 +0200</pubDate>
    <category>SimpleNetworking</category>
    <category>Swift</category>
    <category>SwiftPM</category>
    <description>&lt;p&gt;Today i write about my Swift package &lt;a href="https://github.com/0xWDG/SimpleNetworking"&gt;SimpleNetworking&lt;/a&gt;.&lt;br&gt;
It's a simple networking library for Swift it is a wrapper around URLSession and provides a variety of functions to make easy network requests.&lt;br&gt;
You can find the source code and documentation at GitHub &lt;a href="https://github.com/0xWDG/SimpleNetworking"&gt;https://github.com/0xWDG/SimpleNetworking&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Installation&lt;/h3&gt;
&lt;p&gt;Install using Swift Package Manager&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;dependencies: [
    .package(url: "https://github.com/0xWDG/SimpleNetworking.git", .branch("main")),
],
targets: [
    .target(name: "MyTarget", dependencies: [
        .product(name: "SimpleNetworking", package: "SimpleNetworking"),
    ]),
]&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Async / Await Examples&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SimpleNetworking

class myApiService {
    // Create a shared instance of SimpleNetworking
    let networking = SimpleNetworking.shared

    init() {
        // Set base URL (optional)
        networking.set(serverURL: "https://wesleydegroot.nl")

        // Set a custom user agent (optional)
        networking.set(userAgent: "My App")

        // Set a custom authorization header (optional)
        networking.set(authorization: "MyToken")

        // Set a custom post type (optional)
        networking.set(postType: .json) // .plain, .json, .graphQL
    }

    /// Get a user (GET request)
    func getUser() async {
        let response = await networking.request(
            path: "/api/getUser", 
            method: .get
        )

        print(response.string)
    }

    /// Update a user (POST request)
    func updateUser() async {
        let response = await networking.request(
            path: "/api/updateUser", 
            method: .post([
                "firstname": "Wesley",
                "lastname": "de Groot"
            ])
        )

        print(response.string)
    }

    /// Get all users (GET request)
    func getUsers() async {
        let response = await networking.request(
            path: "/api/getUsers", 
            method: .get
        )

        struct userProfile: Codable {
            let firstname: String
            let lastname: String
        }

        // Decode the response
        let userProfiles: [userProfile]? = networkResponse.decoded()
        print(userProfiles)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Closure Examples&lt;/h3&gt;
&lt;details&gt;
&lt;summary&gt;Closure Examples&lt;/summary&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SimpleNetworking

class myApiService {
    // Create a shared instance of SimpleNetworking
    let networking = SimpleNetworking.shared

    init() {
        // Set base URL (optional)
        networking.set(serverURL: "https://wesleydegroot.nl")

        // Set a custom user agent (optional)
        networking.set(userAgent: "My App")

        // Set a custom authorization header (optional)
        networking.set(authorization: "MyToken")

        // Set a custom post type (optional)
        networking.set(postType: .json) // .plain, .json, .graphQL
    }

    /// Get a user (GET request)
    func getUser() {
        networking.request(
            path: "/api/getUser", 
            method: .get
        ) { response in
            print(response.string)
        }
    }

    /// Update a user (POST request)
    func updateUser() async {
        networking.request(
            path: "/api/updateUser", 
            method: .post([
                "firstname": "Wesley",
                "lastname": "de Groot"
            ])
        ) { response in
            print(response.string)
        }
    }

    /// Get all users (GET request)
    func getUsers() async {
        networking.request(
            path: "/api/getUsers", 
            method: .get
        ) { response in
            struct userProfile: Codable {
                let firstname: String
                let lastname: String
            }

            // Decode the response
            let userProfiles: [userProfile]? = networkResponse.decoded()
            print(userProfiles)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;h3&gt;Debugging&lt;/h3&gt;
&lt;details&gt;
&lt;summary&gt;Enable Debugging&lt;/summary&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SimpleNetworking

/// Debug: print NSURLRequest (Default: false)
SimpleNetworking.shared.debug.requestURL = true
/// Debug: print sent HTTP Headers (Default: false)
SimpleNetworking.shared.debug.requestHeaders = true
/// Debug: print sent Cookies (Default: false)
SimpleNetworking.shared.debug.requestCookies = true
/// Debug: print sent Body (Default: false)
SimpleNetworking.shared.debug.requestBody = true
/// Debug: print received HTTP Headers (Default: false)
SimpleNetworking.shared.debug.responseHeaders = true
/// Debug: print received Body (Default: false)
SimpleNetworking.shared.debug.responseBody = true
/// Debug: print received JSON (if any) (Default: false)
SimpleNetworking.shared.debug.responseJSON = true&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;Example debug output (GET request)&lt;/summary&gt;
&lt;pre&gt;&lt;code&gt;Request:
  GET https://wesleydegroot.nl/

  Headers:
    Content-Type: application/json
    User-Agent: Simple Networking (https://github.com/0xWDG/SimpleNetworking)

  Cookies:

  Body:

HTTPURLResponse:
  HTTP 200
    Cache-Control: max-age=60, private, proxy-revalidate
    Content-Encoding: gzip
    Server: Apache/2
    Content-Type: text/html; charset=UTF-8
    Vary: Accept-Encoding,User-Agent
    Date: Mon, 26 Feb 2024 19:50:42 GMT

  Body:
    ... HTTP BODY ...

  Decoded JSON:
    Unable to parse JSON&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;Example debug output (POST request)&lt;/summary&gt;
&lt;pre&gt;&lt;code&gt;Request:
  POST https://wesleydegroot.nl/api/userUpdate

  Headers:
    User-Agent: Simple Networking (https://github.com/0xWDG/SimpleNetworking)
    Content-Type: application/json

  Cookies:

  Body:
    {"age":33,"firstname":"Wesley","lastname":"de Groot"}

HTTPURLResponse:
  HTTP 200
    Content-Encoding: gzip
    Content-Type: text/html; charset=UTF-8
    Vary: Accept-Encoding,User-Agent
    Cache-Control: max-age=60, private, proxy-revalidate
    Server: Apache/2
    Date: Mon, 26 Feb 2024 19:52:58 GMT

  Body:
    {"status":"user updated","code":200}

  Decoded JSON:
    ["status": user updated, "code": 200]&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;h3&gt;Wrap up&lt;/h3&gt;
&lt;p&gt;SimpleNetworking is a simple networking library for Swift it is a wrapper around URLSession and provides a variety of functions to make easy network requests.&lt;br&gt;
You can find the source code and documentation at GitHub &lt;a href="https://github.com/0xWDG/SimpleNetworking"&gt;https://github.com/0xWDG/SimpleNetworking&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you have any questions or suggestions, feel free to contact me on &lt;a href="https://twitter.com/0xWDG"&gt;Twitter/X&lt;/a&gt; or comment below this post.&lt;/p&gt;</description>
  </item>
  <item>
    <title>Aurora Editor</title>    <link>https://wesleydegroot.nl/blog/aurora-editor</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/aurora-editor</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:41 +0200</pubDate>
    <category>Aurora Editor</category>
    <category>Swift</category>
    <description>&lt;h1&gt;&lt;strong&gt;Aurora Editor: A Swift-Powered IDE for Efficient Coding&lt;/strong&gt;&lt;/h1&gt;
&lt;h3&gt;Introduction&lt;/h3&gt;
&lt;p&gt;As developers, we're always on the lookout for tools that enhance our productivity, streamline our workflows, and make coding a delightful experience. Enter &lt;strong&gt;Aurora Editor&lt;/strong&gt;, a lightweight and fast IDE built by the community, for the community. Let's explore what makes this editor stand out and why it's gaining traction among programmers.&lt;/p&gt;
&lt;h3&gt;What Is Aurora Editor?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Built on Familiar Ground&lt;/strong&gt;: Aurora Editor is not a replacement for Xcode; instead, it's an extensible, superlight editor that leverages the fundamentals of Xcode. If you're comfortable with Xcode, you'll find Aurora Editor's interface familiar and easy to navigate.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Swift-Powered Performance&lt;/strong&gt;: Written entirely in Swift, Aurora Editor boasts lightning-fast performance on macOS. Whether you're working on a small project or a large codebase, Aurora keeps up with your pace.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Key Features&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Efficiency&lt;/strong&gt;: Aurora Editor lets you build projects swiftly and smoothly. Its lightweight design ensures that you spend less time waiting and more time coding.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Extensibility&lt;/strong&gt;: Need additional functionality? Aurora Editor is extensible, allowing you to add custom features or integrate existing plugins seamlessly.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Intuitive Interface&lt;/strong&gt;: Say goodbye to cluttered screens. Aurora's clean interface ensures that you focus on what matters—your code.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Language Support&lt;/strong&gt;: Craft your projects in your favorite programming languages. Aurora supports a wide range of languages, making it versatile for various development tasks.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Why Aurora Editor?&lt;/h3&gt;
&lt;p&gt;As a developer, you might wonder why you should consider Aurora Editor alongside other popular IDEs. Here's why:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Fast&lt;/strong&gt;: If you're a fan of &lt;strong&gt;speed&lt;/strong&gt; then Aurora Editor is your go-to choice. It's designed to be fast and responsive, ensuring that you don't experience any lags or delays.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;One-Stop Solution&lt;/strong&gt;: Aurora Editor is a one-stop solution for all your coding needs. Whether you're working on a personal project or a team-based assignment, Aurora Editor has you covered.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Extensible&lt;/strong&gt;: The ability to extend Aurora Editor's functionality is a game-changer. You can tailor the editor to your specific needs, making it a powerful tool in your arsenal.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Meet the Community&lt;/h3&gt;
&lt;p&gt;The Aurora Editor community is a vibrant group of developers who are passionate about creating a better coding experience. Whether you're a seasoned developer or a beginner, you'll find a welcoming environment where you can share ideas, seek help, and contribute to the editor's growth.&lt;br&gt;
Keep an eye on the Aurora Editor community for updates, tips, and tricks that will enhance your coding journey.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Aurora Editor isn't just an IDE; it's a community-driven tool that prioritizes speed, simplicity, and extensibility. So, next time you're coding, consider giving Aurora Editor a spin. Who knows—you might find yourself creating masterful code with a touch of Aurora magic! ✨&lt;/p&gt;
&lt;h3&gt;Learn More About Aurora Editor&lt;/h3&gt;
&lt;p&gt;Lead maintainers: &lt;a href="https://wesleydegroot.nl"&gt;Wesley de Groot&lt;/a&gt;, &lt;a href="https://github.com/nanashili"&gt;nanashili&lt;/a&gt;.&lt;br&gt;
Website: &lt;a href="https://auroraeditor.com/"&gt;Aurora Editor Website&lt;/a&gt;&lt;br&gt;
GitHub: &lt;a href="https://github.com/auroraeditor"&gt;Aurora Editor on GitHub&lt;/a&gt;&lt;br&gt;
Twitter: &lt;a href="https://twitter.com/Aurora_Editor"&gt;Aurora Editor on Twitter&lt;/a&gt;&lt;br&gt;
Discord: &lt;a href="https://discord.com/invite/5aecJ4rq9D"&gt;Aurora Editor Discord&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Help Aurora Editor Grow&lt;/h3&gt;
&lt;p&gt;If you're passionate about Aurora Editor and want to contribute to its growth, consider joining the community, sharing your ideas, and contributing to the editor's development. Together, we can make Aurora Editor the go-to choice for developers worldwide.&lt;/p&gt;
&lt;h3&gt;Help develop Aurora Editor&lt;/h3&gt;
&lt;p&gt;If you want to help develop Aurora Editor, you can find the source code on &lt;a href="https://github.com/auroraeditor/auroraeditor"&gt;GitHub&lt;/a&gt;, we are always looking for new contributors. no matter if you are a developer, designer, or just want to help out, we are always looking for new contributors.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;References&lt;/em&gt;:&lt;br&gt;
&lt;a href="https://auroraeditor.com/"&gt;Aurora Editor Overview - Aurora Company&lt;/a&gt;&lt;/p&gt;</description>
  </item>
  <item>
    <title>Translating closures to async</title>    <link>https://wesleydegroot.nl/blog/closures-to-async</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/closures-to-async</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:41 +0200</pubDate>
    <category>Async</category>
    <category>Swift</category>
    <description>&lt;p&gt;In this post, we will discuss how to translate closures to async in Swift.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;continuation&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;In Swift, a continuation is a construct that allows you to suspend and resume execution of a piece of code at a later time. It's often used in the context of asynchronous programming.&lt;/p&gt;
&lt;p&gt;When you're dealing with asynchronous tasks, you often need to wait for some operation to complete before you can continue with the rest of your code. This is where continuation comes in.&lt;/p&gt;
&lt;p&gt;You can think of a continuation as a placeholder for the future result of an asynchronous operation. When the operation is complete, you can "resume" the continuation with the result, allowing the rest of your code to proceed.&lt;/p&gt;
&lt;p&gt;In Swift 5.5 and later, continuation is often used in the implementation of async/await patterns. For example, you might use a continuation to wrap a callback-based asynchronous API into a function that can be used with await.&lt;/p&gt;
&lt;h2&gt;Use Case: continuation&lt;/h2&gt;
&lt;p&gt;Imagine you have a function that takes a callback as an argument, and you want to convert it to an async function. Here's an example of what that might look like:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;func fetchData(completion: @escaping (Result&amp;lt;Data, Error&amp;gt;) -&amp;gt; Void) {
    // Perform some asynchronous operation
    // ...
    // When the operation is complete, call the completion handler
    completion(.success(data))
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This function takes a completion handler as an argument, and when the asynchronous operation is complete, it calls the completion handler with the result.&lt;/p&gt;
&lt;p&gt;To convert this function to an async function, you can use a continuation. Here's how you might do that:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;func fetchData() async throws -&amp;gt; Data {
    return try await withCheckedThrowingContinuation { continuation in
        fetchData { result in
            switch result {
            case .success(let data):
                continuation.resume(returning: data)
            case .failure(let error):
                continuation.resume(throwing: error)
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example 2:&lt;/p&gt;
&lt;p&gt;Imagine we have a function whihout &lt;code&gt;Result&lt;/code&gt; type, and we want to convert it to an async function. Here's an example of what that might look like:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;func fetchData(completion: @escaping (Data?, Error?) -&amp;gt; Void) {
    // Perform some asynchronous operation
    // ...
    // When the operation is complete, call the completion handler
    completion(data, error)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To convert this function to an async function, you can use a continuation. Here's how you might do that:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;func fetchData() async throws -&amp;gt; Data {
    return try await withCheckedThrowingContinuation { continuation in
        fetchData { data, error in
            if let data = data {
                continuation.resume(returning: data)
            } else if let error = error {
                continuation.resume(throwing: error)
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;code&gt;withCheckedThrowingContinuation&lt;/code&gt; vs &lt;code&gt;withUnsafeContinuation&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;withCheckedContinuation&lt;/code&gt; will add runtime checks to detect any improper use of the continuation and warn you if it were to happen. &lt;code&gt;withUnsafeContinuation&lt;/code&gt; will perform none of these checks.&lt;/p&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;continuation&lt;/code&gt; must be called exactly once.&lt;/p&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;Continuations are a powerful tool for working with asynchronous code in Swift. They allow you to suspend and resume execution of a piece of code at a later time, making it easier to work with asynchronous operations.&lt;/p&gt;
&lt;p&gt;Resources: &lt;a href="https://developer.apple.com/documentation/swift/checkedcontinuation"&gt;https://developer.apple.com/documentation/swift/checkedcontinuation&lt;/a&gt;&lt;br&gt;
&lt;a href="https://developer.apple.com/documentation/swift/withcheckedcontinuation(function:_:)"&gt;https://developer.apple.com/documentation/swift/withcheckedcontinuation(function:_:)&lt;/a&gt;&lt;/p&gt;</description>
  </item>
  <item>
    <title>Localizing In Xcode</title>    <link>https://wesleydegroot.nl/blog/localizing-in-xcode</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/localizing-in-xcode</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:42 +0200</pubDate>
    <category>Xcode</category>
    <category>Swift</category>
    <category>Localization</category>
    <description>&lt;h2&gt;Localizing and Varying Text with a String Catalog&lt;/h2&gt;
&lt;p&gt;Your app's success hinges on delivering an exceptional experience to users across different locales. &lt;strong&gt;Localization&lt;/strong&gt; is more than just translating text; it involves handling plurals, adapting content for specific devices, and ensuring your app speaks the user's language. In this blog post, we'll explore how to achieve this using &lt;strong&gt;string catalogs&lt;/strong&gt; in Xcode.&lt;/p&gt;
&lt;h3&gt;What Is a String Catalog?&lt;/h3&gt;
&lt;p&gt;A string catalog is a powerful tool that allows you to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Localize&lt;/strong&gt; all your app's text.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Translate&lt;/strong&gt; strings visually within Xcode.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Handle plurals&lt;/strong&gt; for nouns and units.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Vary text&lt;/strong&gt; based on specific devices.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Making Text Localizable&lt;/h3&gt;
&lt;p&gt;Before translation, you need to make your text localizable. Here's how:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Wrap User-Facing Strings&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;In SwiftUI, string literals within a view are automatically localizable:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;// SwiftUI localizable text
Text("Welcome")&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For non SwiftUI, use the &lt;code&gt;String(localized:)&lt;/code&gt; initializer:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;// General localizable text
String(localized: "Buy a book")&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Add Context Comments&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Assist localizers by providing context:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;// Localizable text with comments
Text("Explore", comment: "The title of the tab bar item that navigates to the Explore screen.")

String(localized: "Get Started", comment: "The label on the Get Started button that appears after sign-in.")&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Adding a String Catalog to Your Project&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Create a String Catalog&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Choose &lt;strong&gt;File &amp;gt; New &amp;gt; File&lt;/strong&gt; in Xcode.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Select the platform, enter "string" into the filter field, and choose &lt;strong&gt;String Catalog&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Name it (e.g., "Localizable").&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Xcode will automatically track localizable strings from your code.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Multiple String Catalogs&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;If your catalog grows, create multiple string catalog files within the same project.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Pass the catalog name to localization APIs:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;// SwiftUI pointing to a specific string catalog
Text("Explore", tableName: "Navigation")

// General text pointing to a specific string catalog
String(localized: "Get Started", table: "MainScreen")&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Adding Languages&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;To support multiple languages, add additional languages to your project.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Xcode keeps your string catalog and app in sync during builds.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;String catalogs simplify localization, streamline translations, and enhance the user experience. &lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;References:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/xcode/localizing-and-varying-text-with-a-string-catalog"&gt;https://developer.apple.com/documentation/xcode/localizing-and-varying-text-with-a-string-catalog&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/xcode/adding-support-for-languages-and-regions"&gt;https://developer.apple.com/documentation/xcode/adding-support-for-languages-and-regions&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/xcode/localization"&gt;https://developer.apple.com/documentation/xcode/localization&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Dive into GIT</title>    <link>https://wesleydegroot.nl/blog/git</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/git</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:42 +0200</pubDate>
    <category>git</category>
    <description>&lt;p&gt;Let's dive into the world of &lt;strong&gt;GIT&lt;/strong&gt;. Whether you're a seasoned developer or just starting out, understanding these commands is crucial for efficient version control and collaboration. Below, I'll introduce some essential Git commands that every developer should know:&lt;/p&gt;
&lt;h2&gt;Cloning a Repository&lt;/h2&gt;
&lt;p&gt;Clone an existing repository to your local machine using &lt;code&gt;git clone &amp;lt;repository-url&amp;gt;&lt;/code&gt;.&lt;br&gt;
It creates a copy of the entire project.&lt;br&gt;
note: This does not fetch the submodules, use &lt;code&gt;git clone --recurse-submodules &amp;lt;repository-url&amp;gt;&lt;/code&gt; to fetch the submodules.&lt;/p&gt;
&lt;h2&gt;Creating a Repository&lt;/h2&gt;
&lt;p&gt;Use &lt;code&gt;git init&lt;/code&gt; for initializing a Repository&lt;/p&gt;
&lt;h2&gt;Adding a Remote&lt;/h2&gt;
&lt;p&gt;Use &lt;code&gt;git remote add&lt;/code&gt; for Adding a Remote&lt;br&gt;
e.g. &lt;code&gt;git remote add origin https://github.com/0xWDG/repisotory.git&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;Staging Changes&lt;/h2&gt;
&lt;p&gt;use &lt;code&gt;git add&lt;/code&gt; for Staging Changes&lt;br&gt;
Add specific files or entire directories to the staging area using &lt;code&gt;git add &amp;lt;file&amp;gt;&lt;/code&gt; or &lt;code&gt;git add .&lt;/code&gt;.&lt;br&gt;
A quick (and dirty) way to stage all changes is &lt;code&gt;git add -A .&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Creating a Commit&lt;/h2&gt;
&lt;p&gt;use &lt;code&gt;git commit&lt;/code&gt; for Creating a commit&lt;br&gt;
After staging changes, create a commit (snapshot) with a meaningful message using &lt;code&gt;git commit -m "Your message here"&lt;/code&gt;.&lt;br&gt;
Commits help track the history of your project.&lt;br&gt;
If you want to add all changes and commit in one step, use &lt;code&gt;git commit -am "Your message here"&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Pushing Changes to Remote&lt;/h2&gt;
&lt;p&gt;use &lt;code&gt;git push&lt;/code&gt; for Pushing changes to remote.&lt;br&gt;
This shares your work with others and updates the remote branch.&lt;/p&gt;
&lt;h2&gt;Updating from Remote&lt;/h2&gt;
&lt;p&gt;Pull changes from a remote repository using &lt;code&gt;git pull&lt;/code&gt;.&lt;br&gt;
It fetches and merges the latest changes into your local branch.&lt;/p&gt;
&lt;h2&gt;Current Status&lt;/h2&gt;
&lt;p&gt;use &lt;code&gt;git status&lt;/code&gt; for Checking the current status&lt;br&gt;
It shows which files are modified, staged, or untracked.&lt;/p&gt;
&lt;h2&gt;See commit history&lt;/h2&gt;
&lt;p&gt;View the commit history with &lt;code&gt;git log&lt;/code&gt;.&lt;br&gt;
It displays information about each commit, including the author, date, and commit message.&lt;/p&gt;
&lt;h2&gt;Branching&lt;/h2&gt;
&lt;p&gt;Create a new branch using &lt;code&gt;git branch &amp;lt;branch-name&amp;gt;&lt;/code&gt;.&lt;br&gt;
Switch between branches with &lt;code&gt;git checkout &amp;lt;branch-name&amp;gt;&lt;/code&gt;.&lt;br&gt;
Delete a branch with &lt;code&gt;git branch -d &amp;lt;branch-name&amp;gt;&lt;/code&gt;.&lt;br&gt;
List all branches with &lt;code&gt;git branch&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Combining Branches&lt;/h2&gt;
&lt;p&gt;Merge changes from one branch into another using &lt;code&gt;git merge&lt;/code&gt;.&lt;br&gt;
It combines the commit history of both branches.&lt;/p&gt;
&lt;h2&gt;Reverting Changes&lt;/h2&gt;
&lt;p&gt;Revert changes using &lt;code&gt;git revert&lt;/code&gt;.&lt;br&gt;
e.g. &lt;code&gt;git revert &amp;lt;commit-hash&amp;gt;&lt;/code&gt;.&lt;br&gt;
It creates a new commit that undoes the changes made in the specified commit.&lt;/p&gt;
&lt;h2&gt;Stashing Changes&lt;/h2&gt;
&lt;p&gt;Use &lt;code&gt;git stash&lt;/code&gt; to stash changes.&lt;br&gt;
It temporarily stores changes that are not ready to be committed.&lt;/p&gt;
&lt;h2&gt;Tagging&lt;/h2&gt;
&lt;p&gt;Create a tag using &lt;code&gt;git tag -a &amp;lt;tag-name&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 Tip: Use tags to mark important milestones, such as releases or versions.&lt;br&gt;
e.g. &lt;code&gt;git tag -a v1.0 -m "Version 1.0"&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Push tags to the remote repository using &lt;code&gt;git push --tags&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 Tip: Create the tag after the commit you want to tag.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Tagging a Specific Commit&lt;/h2&gt;
&lt;p&gt;Create a tag for a specific commit using &lt;code&gt;git tag -a &amp;lt;tag-name&amp;gt; &amp;lt;commit-hash&amp;gt;&lt;/code&gt;.&lt;br&gt;
e.g. &lt;code&gt;git tag -a v1.0 9fceb02&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;Ignoring Files&lt;/h2&gt;
&lt;p&gt;Create a &lt;code&gt;.gitignore&lt;/code&gt; file to ignore specific files or directories.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 Tip: Use wildcards to ignore files with a specific extension or pattern.&lt;br&gt;
e.g. &lt;code&gt;*.log&lt;/code&gt; or &lt;code&gt;node_modules/&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Syncing Forks&lt;/h2&gt;
&lt;p&gt;Sync a forked repository with the original repository using &lt;code&gt;git fetch upstream&lt;/code&gt;.&lt;br&gt;
It fetches the latest changes from the original repository.&lt;br&gt;
Then merge the changes into your forked repository using &lt;code&gt;git merge upstream/&amp;lt;branch&amp;gt;&lt;/code&gt;.&lt;br&gt;
Finally, push the changes to your forked repository using &lt;code&gt;git push origin &amp;lt;branch&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;NOTE: You should setup the original repository as a remote using &lt;code&gt;git remote add upstream &amp;lt;repository-url&amp;gt;&lt;/code&gt;.&lt;br&gt;
e.g. &lt;code&gt;git remote add upstream https://github.com/0xWDG/original-repisotory.git&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;details&gt;
&lt;summary&gt;Script to sync fork&lt;/summary&gt;
&lt;p&gt;I use this script to sync my forked repositories with the original repository.&lt;br&gt;
i have it in my &lt;code&gt;~/bin&lt;/code&gt; directory and it is called &lt;code&gt;sync&lt;/code&gt;.&lt;br&gt;
before using it, make sure to make it executable with &lt;code&gt;chmod +x ~/bin/sync&lt;/code&gt; and add &lt;code&gt;~/bin&lt;/code&gt; to your &lt;code&gt;$PATH&lt;/code&gt; variable.&lt;/p&gt;
&lt;p&gt;You can use it like &lt;code&gt;sync&lt;/code&gt; to sync the current branch or &lt;code&gt;sync &amp;lt;branch-name&amp;gt;&lt;/code&gt; to sync a specific branch.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;#/bin/zsh

if [ ! -d ".git" ]; then
    echo "Not a git repository"
    exit
fi

if [ -z "`git remote -v | grep upstream`" ]; then
    echo "Upstream remote not found"
    echo "Add upstream remote with:"
    echo "git remote add upstream https://original/path/to/repo.git"
    exit
fi

if [ -z "$1" ]; then
    BRANCH=`git branch --show-current`
else
    if [ -z "`git branch -l $1`" ]; then
        echo "Branch $1 does not exists"
        exit
    fi

    BRANCH=$1
fi

echo "Syncing with upstream/$BRANCH"
git fetch upstream
git merge upstream/$BRANCH -m "Fork Sync"
git push origin $BRANCH&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;h2&gt;Viewing Changes&lt;/h2&gt;
&lt;p&gt;Use &lt;code&gt;git diff&lt;/code&gt; to view changes between commits, branches, or files.&lt;br&gt;
It shows the differences between the working directory and the staging area.&lt;/p&gt;
&lt;h2&gt;Viewing Remote URLs&lt;/h2&gt;
&lt;p&gt;View the remote URLs with &lt;code&gt;git remote -v&lt;/code&gt;.&lt;br&gt;
It displays the fetch and push URLs for each remote.&lt;/p&gt;
&lt;h2&gt;Viewing Remote Branches&lt;/h2&gt;
&lt;p&gt;View remote branches with &lt;code&gt;git branch -r&lt;/code&gt;.&lt;br&gt;
It lists all remote branches.&lt;/p&gt;
&lt;h2&gt;Viewing Local Branches&lt;/h2&gt;
&lt;p&gt;View local branches with &lt;code&gt;git branch&lt;/code&gt;.&lt;br&gt;
It lists all local branches.&lt;/p&gt;
&lt;h2&gt;Viewing Tags&lt;/h2&gt;
&lt;p&gt;View tags with &lt;code&gt;git tag&lt;/code&gt;.&lt;br&gt;
It lists all tags.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Dangerous but useful commands:&lt;/p&gt;
&lt;h2&gt;Resetting changes on fork to match original repository&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;# pulls all new commits made to upstream/branch
git pull upstream $BRACNH

# this will delete all your local changes to branch
git reset --hard upstream/$BRACNH

# take care, this will delete all your changes on your forked branch
git push origin $BRACNH --force&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Delete all commits&lt;/h2&gt;
&lt;p&gt;Replace &lt;code&gt;$BRANCH&lt;/code&gt; with the name of the branch you want to delete all commits from.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;# This will create a new branch called latest_branch
git checkout --orphan latest_branch

# This adds all the files in the directory to the staging area.
git add -A

# This creates a new commit. (initial commit)
git commit -a -m "Initial commit"

# This deletes the $BRANCH branch
git branch -D "$BRANCH"

# This renames the latest_branch to $BRANCH
git branch -m "$BRANCH"

# This pushes the new branch to the remote repository
git push -f origin "$BRANCH"&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://git-scm.com/doc"&gt;Git Documentation&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://training.github.com"&gt;GitHub Git Cheat Sheet&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>What is @MainActor</title>    <link>https://wesleydegroot.nl/blog/@mainactor</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/@mainactor</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:43 +0200</pubDate>
    <category>MainActor</category>
    <description>&lt;p&gt;In the ever-evolving world of Swift, concurrency has become a hot topic. With the introduction of Swift Concurrency, developers now have powerful tools at their disposal to write more efficient and responsive code. One such tool is the &lt;code&gt;@MainActor&lt;/code&gt; attribute, which allows us to seamlessly execute code on the main queue.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;@MainActor&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;@MainActor&lt;/code&gt; is a &lt;strong&gt;global actor&lt;/strong&gt; that utilizes the main queue for executing its work. In practical terms, this means that methods or types marked with &lt;code&gt;@MainActor&lt;/code&gt; can safely modify the UI because they will always run on the main queue. Additionally, calling &lt;code&gt;MainActor.run()&lt;/code&gt; allows us to push custom work to the main actor, ensuring it executes on the main queue.&lt;/p&gt;
&lt;h2&gt;Use Case: &lt;code&gt;@MainActor&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;In the view model of a SwiftUI app, we often use &lt;code&gt;@Published&lt;/code&gt; properties to update the UI. By marking the entire view model with &lt;code&gt;@MainActor&lt;/code&gt;, we ensure that these UI updates always occur on the main queue. Here's an example:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;@MainActor
class AccountViewModel: ObservableObject {
    @Published var username = "Anonymous"
    @Published var isAuthenticated = false
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The view model is marked with &lt;code&gt;@MainActor&lt;/code&gt;, ensuring that any UI updates triggered by &lt;code&gt;username&lt;/code&gt; or &lt;code&gt;isAuthenticated&lt;/code&gt; always occur on the main queue. This is particularly useful when working with SwiftUI, where UI updates must always occur on the main queue.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct AccountView: View {
    @StateObject var viewModel = AccountViewModel()

    var body: some View {
        VStack {
            Text("Welcome, \(viewModel.username)!")
                .font(.title)
                .padding()

            Text(viewModel.isAuthenticated  ? "You are logged in." : ("You are not logged in.")
                .font(.headline)
                .padding()
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, both &lt;code&gt;username&lt;/code&gt; and &lt;code&gt;isAuthenticated&lt;/code&gt; properties update the UI. By marking the entire class with &lt;code&gt;@MainActor&lt;/code&gt;, we ensure that these UI updates always happen on the main actor. SwiftUI even takes care of this for us when we use &lt;code&gt;@StateObject&lt;/code&gt; or &lt;code&gt;@ObservedObject&lt;/code&gt; inside a view – the whole view runs on the main actor.&lt;/p&gt;
&lt;h2&gt;Why Explicitly Use &lt;code&gt;@MainActor&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;You might wonder: if SwiftUI already ensures main-actor behavior, why bother adding &lt;code&gt;@MainActor&lt;/code&gt; explicitly? Here are a few reasons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Beyond SwiftUI&lt;/strong&gt;: While SwiftUI handles main-actor behavior for observable objects, there are scenarios where our view models might be used outside of SwiftUI – perhaps in UIKit-based parts of our app or in more general Swift contexts. Explicitly marking them with &lt;code&gt;@MainActor&lt;/code&gt; ensures consistent behavior across different contexts.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Async/Await&lt;/strong&gt;: If our view models perform their own asynchronous work (e.g., downloading data from a server using async/await), &lt;code&gt;@MainActor&lt;/code&gt; remains beneficial. It guarantees that UI updates always occur on the main actor.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Opting Out&lt;/strong&gt;: If certain methods or computed properties should &lt;em&gt;not&lt;/em&gt; run on the main actor, we can use &lt;code&gt;nonisolated&lt;/code&gt; – similar to how we do with regular actors.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Global Actor Inference&lt;/h2&gt;
&lt;p&gt;Interestingly, any type containing properties of &lt;code&gt;@MainActor&lt;/code&gt; objects also implicitly becomes &lt;code&gt;@MainActor&lt;/code&gt; using global actor inference. Swift applies precise rules to ensure global-actor behavior works seamlessly without causing unnecessary hurdles.&lt;/p&gt;
&lt;p&gt;In summary, &lt;code&gt;@MainActor&lt;/code&gt; is a powerful tool for making UI updates safer and more predictable. Whether you're building SwiftUI views or working with UIKit, embracing &lt;code&gt;@MainActor&lt;/code&gt; ensures your code dances gracefully on the main stage – the main queue.&lt;/p&gt;
&lt;p&gt;Remember: &lt;strong&gt;actors&lt;/strong&gt; are not suitable for observable objects; they must handle UI updates on the main actor. This is where &lt;code&gt;@MainActor&lt;/code&gt; comes into play, ensuring that UI updates always occur on the main queue.&lt;/p&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;p&gt;While &lt;code&gt;@MainActor&lt;/code&gt; is a powerful tool, it's important to use it judiciously. Overusing &lt;code&gt;@MainActor&lt;/code&gt; can lead to performance issues, as it forces all work to run on the main queue. Always consider whether a given piece of code truly needs to run on the main queue before marking it with &lt;code&gt;@MainActor&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Swift Concurrency has brought a new era of powerful tools to the Swift language. &lt;code&gt;@MainActor&lt;/code&gt; is one such tool, allowing us to ensure that UI updates always occur on the main queue. By marking our types and methods with &lt;code&gt;@MainActor&lt;/code&gt;, we can make our code safer and more predictable, ensuring that UI updates always occur on the main queue.&lt;/p&gt;
&lt;p&gt;Resources:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swift/mainactor"&gt;https://developer.apple.com/documentation/swift/mainactor&lt;/a&gt;&lt;/p&gt;</description>
  </item>
  <item>
    <title>ContentUnavailableView</title>    <link>https://wesleydegroot.nl/blog/contentunavailableview</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/contentunavailableview</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:44 +0200</pubDate>
    <category>SwiftUI</category>
    <category>ContentUnavailableView</category>
    <description>&lt;p&gt;In the ever-evolving world of SwiftUI, creating a seamless user experience involves handling empty states gracefully. Whether it's due to networking failures or empty search results, providing clear feedback to users is crucial. Enter &lt;strong&gt;ContentUnavailableView&lt;/strong&gt;, a powerful SwiftUI component introduced in iOS 17 during WWDC 2023. In this article, we'll explore how to use it effectively and why it's a valuable addition to your toolkit.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;ContentUnavailableView&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;ContentUnavailableView&lt;/strong&gt; is a specialized SwiftUI view designed specifically for presenting empty states. It's your go-to solution when you need to communicate to users that content is unavailable due to various reasons. By leveraging this view, you can maintain consistency across your app while ensuring accessibility and localization.&lt;/p&gt;
&lt;h2&gt;Use Case: Search Empty State&lt;/h2&gt;
&lt;p&gt;In this example, if there are no search results while the query is non-empty, we display the &lt;strong&gt;ContentUnavailableView&lt;/strong&gt;. The resulting view looks familiar to users, as it's widely used across the system. Plus, it automatically translates into the languages your app supports.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct ContentView: View {
    @Bindable
    var pokemon: PokemonViewModel

    var body: some View {
        NavigationStack {
            List(pokemon.pokemon, id: \.self) { pokemon in
                Text(verbatim: pokemon)
            }
            .navigationTitle("Pokémon")
            .overlay {
                if pokemon.pokemon.isEmpty {
                    ContentUnavailableView.search(text: pokemon.query)
                }
            }
            .searchable(text: $pokemon.query)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Use Case: No Internet Connection&lt;/h2&gt;
&lt;details&gt;
&lt;summary&gt;NetworkMonitor.swift&lt;/summary&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI
import Network

final class NetworkMonitor: ObservableObject {
    @Published
    var isConnected = true

    let monitor = NWPathMonitor()

    init() {
        monitor.pathUpdateHandler = { [weak self] path in
            if path.status == .satisfied {
                // Internet connection is available
                self?.isConnected = true
            } else {
                // Internet connection is not available
                self?.isConnected = false
            }
        }

        // Start monitoring
        let queue = DispatchQueue(label: "Monitor")
        monitor.start(queue: queue)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ContentView: View {
    @ObservedObject
    private var network = NetworkMonitor()

    @ObservedObject
    var pokemon: PokemonViewModel

    var body: some View {
        NavigationStack {
            List(pokemon.pokemon, id: \.self) { pokemon in
                Text(verbatim: pokemon)
            }
            .navigationTitle("Pokémon")
            .overlay {
                if !network.isConnected {
                    ContentUnavailableView {
                        Label(
                            "Connection issue", 
                            systemImage: "wifi.slash"
                        )
                    } description: {
                        Text("Check your internet connection")
                    } actions: {
                        Button("Refresh") {
                            pokemon.fetch()
                        }
                    }
                }
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Combined Use Case: No Internet Connection and Empty Search Results&lt;/h2&gt;
&lt;details&gt;
&lt;summary&gt;NetworkMonitor.swift&lt;/summary&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI
import Network

final class NetworkMonitor: ObservableObject {
    @Published
    var isConnected = true

    let monitor = NWPathMonitor()

    init() {
        monitor.pathUpdateHandler = { [weak self] path in
            if path.status == .satisfied {
                // Internet connection is available
                self?.isConnected = true
            } else {
                // Internet connection is not available
                self?.isConnected = false
            }
        }

        // Start monitoring
        let queue = DispatchQueue(label: "Monitor")
        monitor.start(queue: queue)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;PokemonViewModel.swift&lt;/summary&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

class PokemonViewModel: ObservableObject {
    @Published
    var query = "" {
        didSet {
            fetch()
        }
    }

    @Published
    var pokemon: [String] = ["Dragonite", "Pikachu", "Lugia"]

    func fetch() {
        if query.isEmpty {
            pokemon = ["Dragonite", "Pikachu", "Lugia"]
            return
        }

        if query.lowercased().hasPrefix("d") {
            pokemon = ["Dragonite"]
        }

        if query.lowercased().hasPrefix("p") {
            pokemon = ["Pikachu"]
        }

        if query.lowercased().hasPrefix("l") {
            pokemon = ["Lugia"]
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;p&gt;ContentView.swift&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ContentView: View {
    @ObservedObject
    private var network = NetworkMonitor()

    @ObservedObject
    var pokemon = PokemonViewModel()

    var body: some View {
        NavigationStack {
            List(pokemon.pokemon, id: \.self) { pokemon in
                Text(verbatim: pokemon)
            }
            .navigationTitle("Pokémon")
            .overlay {
                if !network.isConnected {
                    ContentUnavailableView {
                        Label(
                            "Connection issue", 
                            systemImage: "wifi.slash"
                        )
                    } description: {
                        Text("Check your internet connection")
                    } actions: {
                        Button("Refresh") {
                            pokemon.fetch()
                        }
                    }
                }
                if network.isConnected &amp;amp;&amp;amp; pokemon.pokemon.isEmpty &amp;amp;&amp;amp; !pokemon.query.isEmpty {
                    ContentUnavailableView.search(text: pokemon.query)
                }
            }
            .searchable(text: $pokemon.query)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="https://wesleydegroot.nl/resources/ContentUnavailableView.swiftpm.zip"&gt;Download the full example&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;p&gt;While &lt;strong&gt;ContentUnavailableView&lt;/strong&gt; is a powerful tool, it's not suitable for all scenarios. especially when handling empty states or situations where your app’s content cannot be displayed, and in complex situations a custom view may be better for empty state handling.&lt;/p&gt;
&lt;h2&gt;Benefits of Using ContentUnavailableView&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Consistency&lt;/strong&gt;: By reusing a standard component, you ensure a consistent look and feel throughout your app.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Localization&lt;/strong&gt;: The view automatically adapts to the languages your app supports.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;SwiftUI Integration&lt;/strong&gt;: It seamlessly integrates with SwiftUI's navigation stack and other components.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;ContentUnavailableView&lt;/strong&gt; is a valuable addition to your SwiftUI toolkit. It's a powerful tool for handling empty states gracefully, ensuring a consistent user experience across your app. By leveraging this view, you can communicate to users that content is unavailable due to various reasons, such as networking failures or empty search results. This results in a more seamless and accessible user experience.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/contentunavailableview"&gt;https://developer.apple.com/documentation/swiftui/contentunavailableview&lt;/a&gt;&lt;/p&gt;</description>
  </item>
  <item>
    <title>Pull-to-Refresh in SwiftUI</title>    <link>https://wesleydegroot.nl/blog/pull-to-refresh-in-swiftui</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/pull-to-refresh-in-swiftui</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:44 +0200</pubDate>
    <category>SwiftUI</category>
    <description>&lt;p&gt;Pull-to-refresh is a common UI pattern in iOS apps.&lt;br&gt;
It allows users to refresh the content of a view by pulling down on it.&lt;br&gt;
In this post, we will learn how to implement pull-to-refresh in SwiftUI, using the &lt;code&gt;refreshable&lt;/code&gt; modifier.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;Pull-to-refresh&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;Pull-to-refresh is a common UI pattern in iOS apps that allows users to refresh the content of a view by pulling down on it.&lt;/p&gt;
&lt;h2&gt;Use Case: Pull to refresh&lt;/h2&gt;
&lt;p&gt;Let’s start with a simple example where you have a List displaying items.&lt;br&gt;
It should also provide a pull-to-refresh gesture to update the list of items.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ContentView: View {
    @State private var items = [String](repeating: "Item", count: 10)

    var body: some View {
        List {
            ForEach(items, id: \.self) { item in
                Text(item)
            }
        }
        .refreshable {
            items = [String](repeating: "Refreshed items", count: 10)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="https://wesleydegroot.nl/resources/pull-to-refresh.swiftpm.zip"&gt;Download the sample code for Swift Playgrounds&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In the example above, we attach the refreshable view modifier to the List view, configuring the pull-to-refresh gesture.&lt;/p&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;SwiftUI's &lt;code&gt;refreshable&lt;/code&gt; modifier makes it easy to add pull-to-refresh to your app.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In this post, we learned how to implement pull-to-refresh in SwiftUI, using the &lt;code&gt;refreshable&lt;/code&gt; modifier.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/refreshable"&gt;Apple Developer Documentation&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>TipKit</title>    <link>https://wesleydegroot.nl/blog/tipkit</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/tipkit</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:45 +0200</pubDate>
    <category>TipKit</category>
    <category>Swift</category>
    <description>&lt;p&gt;Let's delve into the fascinating world of &lt;strong&gt;TipKit&lt;/strong&gt;, a powerful framework that empowers developers to create and customize app tips.&lt;br&gt;
Whether you're aiming to introduce new features, reveal hidden functionalities, or enhance user productivity, TipKit has got you covered.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;What Is TipKit?&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;TipKit is a &lt;strong&gt;framework&lt;/strong&gt; designed by Apple that enables you to seamlessly integrate &lt;strong&gt;contextual tips&lt;/strong&gt; into your app.&lt;br&gt;
These tips serve as educational moments, guiding users toward discovering features they might not have stumbled upon organically.&lt;br&gt;
With TipKit, you can:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Customize Appearance&lt;/strong&gt;: Tailor the look and feel of your tips to match your app's design.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Define Content&lt;/strong&gt;: Craft engaging messages that highlight specific features or provide useful information.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Control Eligibility&lt;/strong&gt;: Specify conditions for when tips should appear, ensuring they reach the right audience at the right time.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;&lt;strong&gt;Creating Your First Tip&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Let's dive into creating a simple tip using SwiftUI.&lt;br&gt;
Imagine we want to highlight the "Save as a Favorite" feature in our app.  &lt;/p&gt;
&lt;p&gt;Here's how you can do it:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI
import TipKit

// Define your tip's content
struct TipKitTip: Tip {
    var title: Text {
        Text("Title")
    }
    var message: Text? {
        Text("Message")
    }
    var image: Image? {
        Image(systemName: "star")
    }
}

struct ContentView: View {
    // Create an instance of your tip
    var tipKitTip = TipKitTip()

    var body: some View {
        VStack {
            // Place the tip view near the feature you want to highlight
            TipView(tipKitTip, arrowEdge: .bottom)
            Image(systemName: "star")
                .imageScale(.large)
            Spacer()
        }
        .task {
            // Configure and load your tips at app launch
            try? Tips.configure([
                .displayFrequency(.immediate),
                .datastoreLocation(.applicationDefault)
            ])
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="https://wesleydegroot.nl/resources/TipKit.swiftpm.zip"&gt;Download the sample code for Swift Playgrounds&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Best Practices for Using Tips&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Be Selective&lt;/strong&gt;: Use tips sparingly to highlight non-obvious features. Avoid overwhelming users with too many tips.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Avoid Overuse&lt;/strong&gt;: Don't display tips every time someone uses your app. They can become distracting.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;No Guided Tours&lt;/strong&gt;: Tips are not meant for guiding users through your app step-by-step.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Effective Targeting&lt;/strong&gt;: Use eligibility rules and display frequency to ensure tips reach the right audience.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Remember, TipKit is a powerful tool, but like any tool, it's most effective when used thoughtfully.&lt;/p&gt;
&lt;p&gt;Resources:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/TipKit"&gt;https://developer.apple.com/documentation/TipKit&lt;/a&gt;&lt;/p&gt;</description>
  </item>
  <item>
    <title>Implementing Sign in with Apple</title>    <link>https://wesleydegroot.nl/blog/implementing-sign-in-with-apple</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/implementing-sign-in-with-apple</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:45 +0200</pubDate>
    <category>Sign in with Apple</category>
    <category>SwiftUI</category>
    <description>&lt;p&gt;&lt;strong&gt;Sign in with Apple&lt;/strong&gt; is a user-friendly authentication method that allows users to sign in to apps and websites using their Apple ID.&lt;br&gt;
In this blog post, we'll explore how to integrate Sign in with Apple into your Swift-based iOS app for UIKit and SwiftUI based apps.&lt;/p&gt;
&lt;h2&gt;Overview&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Sign in with Apple&lt;/strong&gt; is a powerful authentication solution that simplifies user access to apps and websites.  &lt;/p&gt;
&lt;p&gt;Here's what you need to know:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Easy Sign-In&lt;/strong&gt;: Instead of cumbersome forms and password management, users can sign in using their &lt;strong&gt;Apple ID&lt;/strong&gt;. No more remembering passwords or verifying email addresses!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Privacy First&lt;/strong&gt;: Apple prioritizes user privacy. Data collection is limited to the user's &lt;strong&gt;name and email address&lt;/strong&gt;. Plus, Apple's private email relay ensures that users receive emails while keeping their address confidential.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Two-Factor Authentication&lt;/strong&gt;: Every account using Sign in with Apple is automatically protected with &lt;strong&gt;two-factor authentication&lt;/strong&gt;. Users on Apple devices can reauthenticate anytime with &lt;strong&gt;Face ID&lt;/strong&gt; or &lt;strong&gt;Touch ID&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Cross-Platform Compatibility&lt;/strong&gt;: Sign in with Apple works seamlessly across &lt;strong&gt;iOS, iPadOS, macOS, tvOS, and watchOS&lt;/strong&gt;. It's also compatible with any browser, making it suitable for web and app development.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Antifraud Measures&lt;/strong&gt;: Detect fake accounts with Apple's on-device machine learning. Get a privacy-friendly signal to identify genuine users.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;Before diving into implementation, ensure you have the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Apple Developer Account&lt;/strong&gt;: You'll need an active Apple Developer account to configure Sign in with Apple.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Xcode&lt;/strong&gt;: Make sure you have Xcode installed on your development machine.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Step-by-Step Implementation&lt;/h2&gt;
&lt;h3&gt;1. Configure Your Project&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open your project in Xcode.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Navigate to the &lt;strong&gt;Signing &amp;amp; Capabilities&lt;/strong&gt; pane.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Set a unique &lt;strong&gt;bundle identifier&lt;/strong&gt; for your app.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add your Apple ID account and assign the target to a team (enabling Sign in with Apple capability).&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;2. Add the Sign in with Apple Button&lt;/h3&gt;
&lt;p&gt;In your view controller or SwiftUI view, add the Sign in with Apple button. Here's how:&lt;/p&gt;
&lt;h4&gt;UIKit (View Controller)&lt;/h4&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import AuthenticationServices

class LoginViewController: UIViewController {
    func setupProviderLoginView() {
        let authorizationButton = ASAuthorizationAppleIDButton(
            type: .signIn,
            style: .whiteOutline
        )

        authorizationButton.addTarget(
            self,
            action: #selector(handleAuthorizationAppleIDButtonPress),
            for: .touchUpInside
        )

        self.addSubview(authorizationButton)
    }

    @objc func handleAuthorizationAppleIDButtonPress() {
        let appleIDProvider = ASAuthorizationAppleIDProvider()
        let request = appleIDProvider.createRequest()
        request.requestedScopes = [.fullName, .email]

        let authorizationController = ASAuthorizationController(authorizationRequests: [request])
        authorizationController.delegate = self
        authorizationController.presentationContextProvider = self
        authorizationController.performRequests()
    }
}

// MARK: - Sign in with Apple
extension LoginViewController: ASAuthorizationControllerDelegate,
                               ASAuthorizationControllerPresentationContextProviding {
    func presentationAnchor(for controller: ASAuthorizationController) -&amp;gt; ASPresentationAnchor {
        return self.view.window!
    }

    func authorizationController(
        controller: ASAuthorizationController,
        didCompleteWithAuthorization authorization: ASAuthorization
    ) {
        switch authorization.credential {
        case let appleIDCredential as ASAuthorizationAppleIDCredential:
            // Create an account in your system.
            let userIdentifier = appleIDCredential.user
            let fullName = appleIDCredential.fullName
            let email = appleIDCredential.email
            let JWTToken = appleIDCredential.identityToken
            let authCode = appleIDCredential.authorizationCode

            dump(appleIDCredential)
            // Close login window.

        case let passwordCredential as ASPasswordCredential:
            // Sign in using an existing iCloud Keychain credential.
            let username = passwordCredential.user
            let password = passwordCredential.password

            print(passwordCredential)
            // Close login window

        default:
            break
        }
    }

    func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
        // Handle error.
        print("Something went wrong", error)
    }

    func performExistingAccountSetupFlows() {
        // Prepare requests for both Apple ID and password providers.
        let requests = [ASAuthorizationAppleIDProvider().createRequest(),
                        ASAuthorizationPasswordProvider().createRequest()]

        // Create an authorization controller with the given requests.
        let authorizationController = ASAuthorizationController(authorizationRequests: requests)
        authorizationController.delegate = self
        authorizationController.presentationContextProvider = self
        authorizationController.performRequests()
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;SwiftUI&lt;/h4&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI
import AuthenticationServices

struct ContentView: View {
    var body: some View {
        SignInWithAppleButton(.signIn) { request in
            request.requestedScopes = [.fullName, .email]
        } onCompletion: { result in
            switch result {
            case .success(let authorization):
                if let userCredential = authorization.credential as? ASAuthorizationAppleIDCredential {
                    dump(userCredential)
                }

            case .failure(let error):
                print("Could not authenticate: \(error.localizedDescription)")
            }
        }
        .signInWithAppleButtonStyle(.whiteOutline)
        .frame(width: 280, height: 45)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="https://wesleydegroot.nl/resources/Implementing-Sign-in-with-Apple.swiftpm.zip"&gt;Download SwiftUI Playground&lt;/a&gt;&lt;/p&gt;
&lt;h5&gt;&lt;code&gt;SignInWithAppleButton(.label)&lt;/code&gt;&lt;/h5&gt;
&lt;p&gt;&lt;code&gt;.continue&lt;/code&gt;: Continue label&lt;br&gt;
&lt;code&gt;signIn&lt;/code&gt;: Sign in with Apple label on the button.&lt;br&gt;
&lt;code&gt;signUp&lt;/code&gt;: Sign up with Apple label on the button.&lt;/p&gt;
&lt;h5&gt;&lt;code&gt;.signInWithAppleButtonStyle(.style)&lt;/code&gt;&lt;/h5&gt;
&lt;p&gt;&lt;code&gt;black&lt;/code&gt;: black button.&lt;br&gt;
&lt;code&gt;white&lt;/code&gt;: white button.&lt;br&gt;
&lt;code&gt;whiteOutline&lt;/code&gt;: white-outlined button.&lt;/p&gt;
&lt;h5&gt;&lt;code&gt;Error codes&lt;/code&gt;&lt;/h5&gt;
&lt;p&gt;&lt;code&gt;ASAuthorizationError.unknown&lt;/code&gt;: Unknown error.&lt;br&gt;
&lt;code&gt;ASAuthorizationError.canceled&lt;/code&gt;: User canceled the request.&lt;br&gt;
&lt;code&gt;ASAuthorizationError.failed&lt;/code&gt;: Authorization failed.&lt;br&gt;
&lt;code&gt;ASAuthorizationError.invalidResponse&lt;/code&gt;: Invalid response.&lt;br&gt;
&lt;code&gt;ASAuthorizationError.notHandled&lt;/code&gt;: Request not handled.&lt;br&gt;
&lt;code&gt;ASAuthorizationError.unknown&lt;/code&gt;: Unknown error.&lt;/p&gt;
&lt;h3&gt;3. User is signed in&lt;/h3&gt;
&lt;p&gt;Cache the name and email address of the user, this will be given once to the application. &lt;/p&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;p&gt;The user's name and email are only given once, this is expected behaviour.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It is recommened that you securely cache the initial ASAuthorizationAppleIDCredential containing the user info until you can validate that an account has succesfully been created on your server.&lt;br&gt;
Source: &lt;a href="https://forums.developer.apple.com/thread/121496#379297"&gt;https://forums.developer.apple.com/thread/121496#379297&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Server-side validation may be needed, depending on your needs.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Sign in with Apple provides a seamless and secure authentication experience for your users.&lt;br&gt;
It's easy to implement for usage in your app.&lt;/p&gt;
&lt;p&gt;Resources&lt;/p&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/authenticationservices/implementing_user_authentication_with_sign_in_with_apple"&gt;https://developer.apple.com/documentation/authenticationservices/implementing_user_authentication_with_sign_in_with_apple&lt;/a&gt;&lt;/p&gt;</description>
  </item>
  <item>
    <title>Default values for UserDefaults</title>    <link>https://wesleydegroot.nl/blog/default-values-for-userdefaults</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/default-values-for-userdefaults</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:46 +0200</pubDate>
    <category>Swift</category>
    <category>UserDefaults</category>
    <description>&lt;p&gt;UserDefaults (&lt;code&gt;NSUserDefaults&lt;/code&gt;) is a go-to database for saving users' preferences over application behavior.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;UserDefaults&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;UserDefaults&lt;/code&gt; is a simple key-value store that allows you to save small amounts of data such as user preferences, settings, and other application state information. It is commonly used to store user settings, such as the user's preferred language, theme, or notification preferences.&lt;/p&gt;
&lt;h2&gt;Use Case: &lt;code&gt;UserDefaults.register&lt;/code&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;let userDefaults = UserDefaults.standard
userDefaults.register(
    defaults: [
        "selectedPokemon": "Pikachu",
        "pokemonCanEvolve": true
    ]
)

userDefaults.bool(forKey: "pokemonCanEvolve") // true.&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning&lt;/strong&gt;: The &lt;code&gt;register(defaults:)&lt;/code&gt; method only sets the default values if the key does not exist in the user defaults, it will not save the default values to the user defaults. Therefore, you should call this method each time your app launches to ensure that the default values are set.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;UserDefaults saves data in a local plist file on disk.&lt;br&gt;
This means that the user can access and modify the data if they have access to the device, therefore it is not suitable for storing sensitive information such as passwords or tokens.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Registering default values will not save the default values to disk, therefore you should call this method each time your app launches to ensure that the default values are set.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;UserDefaults is not suitable for large data storage; consider using Core Data for that purpose.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;NSUbiquitousKeyValueStore&lt;/code&gt; which is the cloud-enabled &lt;code&gt;UserDefaults&lt;/code&gt; that can be used to synchronize data between devices using iCloud, has a limit of 1MB for the total size of the data, and a limit of 1024 keys.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;UserDefaults is a simple way to store small amounts of data such as user preferences, settings, and other application state information. It is not suitable for storing sensitive information or large amounts of data.&lt;/p&gt;
&lt;p&gt;Resources:&lt;br&gt;
&lt;a href="https://developer.apple.com/documentation/foundation/userdefaults"&gt;https://developer.apple.com/documentation/foundation/userdefaults&lt;/a&gt;&lt;/p&gt;</description>
  </item>
  <item>
    <title>SwiftUI ViewModifiers</title>    <link>https://wesleydegroot.nl/blog/swiftui-viewmodifiers</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/swiftui-viewmodifiers</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:46 +0200</pubDate>
    <category>SwiftUI</category>
    <category>ViewModifiers</category>
    <description>&lt;p&gt;SwiftUI ViewModifiers are a powerful tool for customizing and enhancing views in your app. They allow you to encapsulate common view modifications into reusable, composable units, making it easy to apply the same changes to multiple views throughout your app&lt;/p&gt;
&lt;h2&gt;What is a &lt;code&gt;View modifier&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;View modifier is a very important concept of SwiftUI. If you have a chance to play around with SwiftUI you might have seen one already like &lt;code&gt;.background&lt;/code&gt;, &lt;code&gt;.padding&lt;/code&gt;, ...&lt;/p&gt;
&lt;p&gt;SwiftUI already provided plenty of modifiers, but you can also create a custom one with a simple protocol, &lt;code&gt;ViewModifier&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Example 1: Blue Border&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct BlueBorder: ViewModifier {
    func body(content: Content) -&amp;gt; some View {
        return content
            .padding(2)
            .border(.blue, width: 1)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use it like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;Import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            Text("Hello, World!")
                .modifier(BlueBorder())
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or you can create an extension to make it more readable:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;extension View {
    @warn_unqualified_access
    func blueBorder() -&amp;gt; some View {
        self.modifier(BlueBorder())
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then you can use it like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;Text("Hello, World!")
    .blueBorder()&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Example 2: Card view&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct CardBackground: ViewModifier {
    func body(content: Content) -&amp;gt; some View {
        content
            .padding(10)
            .background(
                Color(UIColor.systemGroupedBackground)
            )
            .cornerRadius(20)
            .shadow(
                color: Color.black.opacity(0.2),
                radius: 4
            )
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use it like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;Import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            Text("Hello, World!")
                .modifier(CardBackground())
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or you can create an extension to make it more readable:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;extension View {
    @warn_unqualified_access
    func cardBackground() -&amp;gt; some View {
        self.modifier(CardBackground())
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then you can use it like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;Import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            Text("Hello, World!")
                .cardBackground()
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Example 3: Custom Button&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct CustomButton: ViewModifier {
    func body(content: Content) -&amp;gt; some View {
        content
            .padding(10)
            .background(Color.blue)
            .foregroundColor(.white)
            .cornerRadius(20)
            .shadow(
                color: Color.black.opacity(0.2),
                radius: 4
            )
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use it like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;Import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            Button("Hello, World!") {
                print("Hello, World!")
            }.modifier(CustomButton())
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or you can create an extension to make it more readable:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;extension View {
    @warn_unqualified_access
    func customButton() -&amp;gt; some View {
        self.modifier(CustomButton())
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then you can use it like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;Import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            Button("Hello, World!") {
                print("Hello, World!")
            }.customButton()
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Example 4: Spoiler support&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct Spoiler: ViewModifier {
    // visibility of the spoiler warning
    @State var isHidden: Bool = true

    func body(content: Content) -&amp;gt; some View {
        // A ZStack is a view that overlays its children
        ZStack(
            alignment: .center, 
            content: {            
            if isHidden {
                // User can't see the content.

                // Blur the content
                content
                    .layoutPriority(1)
                    .blur(radius: 30)
                    .clipped()

                // Add a spoiler warning
                VStack {
                    Image(
                        systemName: "eye.slash.fill"
                    )
                        .foregroundColor(.white)

                    Text("Spoiler")
                        .font(.caption)
                        .bold()
                        .foregroundColor(.white)
                }
            } else {
                // User can see the content
                content
            }
        }).onTapGesture {
            withAnimation { 
                self.isHidden = !self.isHidden
            }            
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use it like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;Import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            // In this example, 
            // we use the SF Symbol "rainbow" as the spoiler content.
            // It's animated and multi-color.
            // And resized to 250wx150h.
            Image(systemName: "rainbow")
                // Make the SF Symbol multi-color
                .renderingMode(.original)
                // Make the image resizable
                .resizable()
                // Resize to 250wx150h
                .frame(width: 250, height: 150)
                // (optional) animate the rainbow
                .symbolEffect(.variableColor)
                .modifier(Spoiler())
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or you can create an extension to make it more readable:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;extension View {
    @warn_unqualified_access
    func spoiler() -&amp;gt; some View {
        self.modifier(Spoiler())
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then you can use it like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;Text("Hello, World!")
    .spoiler()&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;In action&lt;/h2&gt;
&lt;p&gt;Here is a visual representation of the examples above:&lt;br&gt;
&lt;img src="/resources/SwiftUI-ViewModifiers.png" alt="ViewModifiers" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;Download the &lt;a href="/resources/SwiftUI-ViewModifiers.swiftpm.zip"&gt;Swift Playground here&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Watch out for missing qualifiers, we eliminated risk in the examples, but i recommend you to read &lt;a href="https://blog.eidinger.info/make-your-swiftui-view-modifiers-safer"&gt;this article&lt;/a&gt; in the resources section to understand the risks.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;SwiftUI ViewModifiers are a powerful tool for customizing and enhancing views in your app. They allow you to encapsulate common view modifications into reusable, composable units, making it easy to apply the same changes to multiple views throughout your app.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/reducing-view-modifier-maintenance"&gt;https://developer.apple.com/documentation/swiftui/reducing-view-modifier-maintenance&lt;/a&gt;&lt;br&gt;
This is the documentation about the View Modifiers from apple&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://www.danijelavrzan.com/posts/2023/02/card-view-swiftui/"&gt;https://www.danijelavrzan.com/posts/2023/02/card-view-swiftui/&lt;/a&gt;&lt;br&gt;
I used this post from Danijela Vrzan to create the &lt;code&gt;CardBackground&lt;/code&gt; example.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://blog.eidinger.info/make-your-swiftui-view-modifiers-safer"&gt;https://blog.eidinger.info/make-your-swiftui-view-modifiers-safer&lt;/a&gt;&lt;br&gt;
I used this post to understand the risks of missing qualifiers in the ViewModifiers.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://sarunw.com/posts/swiftui-viewmodifier/"&gt;https://sarunw.com/posts/swiftui-viewmodifier/&lt;/a&gt;&lt;br&gt;
I used this post to create the &lt;code&gt;Spoiler&lt;/code&gt; example.&lt;br&gt;
The code is based on the &lt;code&gt;NSFW&lt;/code&gt; example in the post of Sarunw.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Subscripts in Swift</title>    <link>https://wesleydegroot.nl/blog/subscript-in-swift</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/subscript-in-swift</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:47 +0200</pubDate>
    <category>Swift</category>
    <description>&lt;p&gt;Subscripts provide a convenient way to access the elements of a collection, list, or sequence directly by index. Whether you’re dealing with arrays, dictionaries, or custom types, subscripts allow you to set and retrieve values without needing separate methods for each operation.&lt;/p&gt;
&lt;h2&gt;What is a &lt;code&gt;subscript&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;Subscripts are shortcuts for accessing the member elements of a collection, list, or sequence. They are used to set and retrieve values without needing separate methods for each operation.&lt;/p&gt;
&lt;h2&gt;Use Case of a &lt;code&gt;subscript&lt;/code&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;extension Collection {
    // Support collection[safe: number]
    subscript (safe index: Index) -&amp;gt; Element? {
        return indices.contains(index) ? self[index] : nil
    }

    // Support collection[safe: number, default: "some default value"]
    subscript (safe index: Index, default value: Element) -&amp;gt; Element {
        return indices.contains(index) ? self[index] : value
    }
}

let pokemon = ["Pikachu", "Lugia", "Dragonite", "Mewtwo"]

pokemon[10] // will crash 💥
pokemon[safe: 10] // returns nil
pokemon[safe: 10, default: "Pikachu"] // returns Pikachu.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Download the &lt;a href="/resources/Subscripts-in-Swift.swiftpm.zip"&gt;Swift Playground here&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Subscripts provide a convenient way to access the elements of a collection, list, or sequence directly by index. Whether you’re dealing with arrays, dictionaries, or custom types, subscripts allow you to set and retrieve values without needing separate methods for each operation.&lt;/p&gt;
&lt;p&gt;Resources:&lt;br&gt;
&lt;a href="https://docs.swift.org/swift-book/documentation/the-swift-programming-language/subscripts/"&gt;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/subscripts/&lt;/a&gt;&lt;/p&gt;</description>
  </item>
  <item>
    <title>How to monitor network in SwiftUI</title>    <link>https://wesleydegroot.nl/blog/monitor-network-in-swiftui</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/monitor-network-in-swiftui</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:47 +0200</pubDate>
    <category>SwiftUI</category>
    <category>Network</category>
    <description>&lt;p&gt;Network monitoring is crucial for maintaining robust and reliable applications.&lt;br&gt;
In this blog post, we’ll explore how to implement network monitoring in Swift using Apple’s native Network framework.&lt;br&gt;
By leveraging this framework, you can efficiently track network connectivity, detect changes, and respond accordingly.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;NWPathMonitor&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;NWPathMonitor is a class that monitors changes to network connectivity.&lt;br&gt;
It provides information about the current network path, such as the status of the network connection and the available interfaces.&lt;/p&gt;
&lt;h2&gt;Writing the monitor&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import SwiftUI
import Network

final class NetworkMonitor: ObservableObject {
    // This will be used to track the network connectivity
    @Published
    var isConnected = true

    // This will be used to track if the network is expensive (e.g. cellular data)
    @Published
    var isExpensive = false

    @Published
    var networkType: NWInterface.InterfaceType? = .other

    // This will be used to track the network path (e.g. Wi-Fi, cellular data, etc.)
    @Published
    var nwPath: NWPath?

    // Create an instance of NWPathMonitor
    let monitor = NWPathMonitor()

    init() {
        // Set the pathUpdateHandler
        monitor.pathUpdateHandler = { [weak self] path in

            // Check if the device is connected to the internet
            self?.isConnected = path.status == .satisfied

            // Check if the network is expensive (e.g. cellular data)
            self?.isExpensive = path.isExpensive

            // Check which interface we are currently using
            self?.networkType = path.availableInterfaces.first?.type

            // Update the network path
            self?.nwPath = path
        }

        // Create a queue for the monitor
        let queue = DispatchQueue(label: "Monitor")

        // Start monitoring
        monitor.start(queue: queue)
    }

    deinit {
        // Stop monitoring
        monitor.cancel()
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Use the monitor&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI


struct ContentView: View {
    @ObservedObject
    private var network = NetworkMonitor()

    var body: some View {
        VStack {
            Text("Hello!")
            Text("The network status is \(network.isConnected ? "Connected" : "Disconnected")")
            Text("You are using a \"\(network.isExpensive ? "Expensive" : "Normal")\" internet connection")

            HStack(spacing: 0) {
                Text("You are using \"")
                switch (network.networkType) {
                case .cellular:
                    Text("Celluar")
                case .wifi:
                    Text("Wifi")
                case .loopback:
                    Text("Loopback")
                case .other:
                    Text("Other")
                case .wiredEthernet:
                    Text("Wired")
                default:
                    Text("Unknown")
                }
                Text("\" to connect to the internet")
            }
        }.task {
            print(network.nwPath)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Download the &lt;a href="/resources/Monitor-network-in-SwiftUI.swiftpm.zip"&gt;Swift Playground here&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Network monitoring is crucial for maintaining robust and reliable applications.&lt;br&gt;
In this blog post, we explored how to implement network monitoring in Swift using Apple’s native Network framework.&lt;br&gt;
By leveraging this framework, you can efficiently track network connectivity, detect changes, and respond accordingly.&lt;/p&gt;
&lt;p&gt;Resources:&lt;br&gt;
&lt;a href="https://developer.apple.com/documentation/network"&gt;https://developer.apple.com/documentation/network&lt;/a&gt;&lt;/p&gt;</description>
  </item>
  <item>
    <title>LabeledContent in SwiftUI</title>    <link>https://wesleydegroot.nl/blog/labeledcontent-in-swiftui</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/labeledcontent-in-swiftui</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:48 +0200</pubDate>
    <category>SwiftUI</category>
    <category>LabeledContent</category>
    <description>&lt;p&gt;As developers, we often need to display a value alongside a label, and manually arranging this can be difficult.&lt;br&gt;
But &lt;code&gt;LabeledContent&lt;/code&gt;, a powerful and versatile view that simplifies this common UI pattern.&lt;/p&gt;
&lt;h2&gt;What Is &lt;code&gt;LabeledContent&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;At its core, &lt;code&gt;LabeledContent&lt;/code&gt; is a straightforward view that combines a label and content.&lt;br&gt;
It's like a dynamic duo that works together seamlessly.&lt;br&gt;
Let's dive into how it works and explore some practical examples.&lt;/p&gt;
&lt;h3&gt;Basic Usage&lt;/h3&gt;
&lt;p&gt;The basic usage of &lt;code&gt;LabeledContent&lt;/code&gt; involves providing a label and a corresponding value.&lt;br&gt;
Here's a simple example:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct ContentView: View {
    var body: some View {
        Form {
            LabeledContent("Label", value: "Content")
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the example below we use different ways to create some &lt;code&gt;LabeledContent&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct ContentView: View {
    var body: some View {
        Form {
            Section {
                // Version 1: String label &amp;amp; String value
                LabeledContent("Pikachu", value: "#25")

                // Version 2: String label &amp;amp; formatted value
                LabeledContent("Pikachu", value: 25, format: .number)

                // Version 3: String label &amp;amp; any View as value
                LabeledContent("Region") {
                    Image(systemName: "globe.europe.africa.fill")
                    Text("Kanto")
                }

                // Version 4: Any View as value &amp;amp; any View as a label
                LabeledContent {
                    VStack(alignment: .leading) {
                        Text("#025 Pikachu")
                        Text("#149 Dragonite")
                        Text("#249 Lugia")
                        Text("#145 Zapdos")
                        Text("#150 Mewtwo")
                        Text("#132 Ditto")
                    }
                } label: {
                    Text("Team")
                    Text("#1")
                }
            }
        }
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Download the &lt;a href="/resources/LabeledContent.swiftpm.zip"&gt;Swift Playground here&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In summary, &lt;code&gt;LabeledContent&lt;/code&gt; is a powerful tool for creating well-organized and visually appealing UIs.&lt;br&gt;
Whether you're displaying simple strings or complex views, it streamlines the process and adheres to design guidelines.&lt;/p&gt;
&lt;p&gt;Resources:&lt;br&gt;
&lt;a href="https://developer.apple.com/documentation/swiftui/labeledcontent/"&gt;https://developer.apple.com/documentation/swiftui/labeledcontent/&lt;/a&gt;&lt;/p&gt;</description>
  </item>
  <item>
    <title>ExpressibleByStringLiteral URL</title>    <link>https://wesleydegroot.nl/blog/expressiblebystringliteral-url</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/expressiblebystringliteral-url</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:49 +0200</pubDate>
    <category>Swift</category>
    <category>ExpressibleByStringLiteral</category>
    <description>&lt;p&gt;The &lt;code&gt;ExpressibleByStringLiteral&lt;/code&gt; protocol is a powerful feature in Swift that allows us to create custom types directly from string literals. Essentially, it enables us to initialize instances of our custom types using string values. This can be incredibly useful for scenarios where we want to represent certain data types more intuitively or avoid force unwrapping when dealing with known valid strings.&lt;/p&gt;
&lt;h2&gt;Writing the extension &lt;code&gt;ExpressibleByStringLiteral&lt;/code&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;/// Lets URLs be expressed conveniently with literal strings:
/// # Example:
///     let baseUrl: URL = "https://wesleydegroot.nl/blog"
extension URL: ExpressibleByStringLiteral {
    public init(stringLiteral string: StaticString) {
        guard let url = URL(string: "\(string)") else {
            preconditionFailure("Invalid literal URL string: \(string)")
        }
        self = url
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Use Case: &lt;code&gt;ExpressibleByStringLiteral&lt;/code&gt; for URLs&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;let baseUrl: URL = "https://wesleydegroot.nl/blog"
print(baseUrl.absoluteString) // Output: "https://wesleydegroot.nl/blog"&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In summary, &lt;code&gt;ExpressibleByStringLiteral&lt;/code&gt; empowers us to make our custom types more expressive and convenient to work with. By conforming to this protocol, we can simplify our code and enhance readability.&lt;/p&gt;
&lt;p&gt;Remember to explore other use cases and experiment with different types to harness the full potential of &lt;code&gt;ExpressibleByStringLiteral&lt;/code&gt; in your Swift projects!&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swift/expressiblebystringliteral"&gt;Apple Documentation&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>self, Self, and Self.self in Swift</title>    <link>https://wesleydegroot.nl/blog/self-self-self.self-in-swift</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/self-self-self.self-in-swift</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:49 +0200</pubDate>
    <category>Swift</category>
    <category>self</category>
    <description>&lt;p&gt;Let's dive into the fascinating world of &lt;code&gt;self&lt;/code&gt;, &lt;code&gt;Self&lt;/code&gt;, and &lt;code&gt;Self.self&lt;/code&gt; in Swift.&lt;br&gt;
These seemingly similar constructs have distinct meanings and use cases.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;self&lt;/code&gt; (lowercase)?&lt;/h2&gt;
&lt;p&gt;When you're inside an instance method or property of a class, &lt;code&gt;self&lt;/code&gt; refers to the &lt;strong&gt;current instance&lt;/strong&gt; of that class.&lt;br&gt;
It's like saying, "Hey, I'm talking about myself!"  &lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;class Pokemon {
    var name: String

    init(name: String) {
        self.name = name
    }

    func introduce() {
        print("Hello, my name is \(self.name).")
    }
}

let dragonite = Pokemon(name: "Dragonite")
dragonite.introduce() // Prints: "Hello, my name is Dragonite."&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;What is &lt;code&gt;Self&lt;/code&gt; (capital 'S')?&lt;/h2&gt;
&lt;p&gt;When you're working with protocols and protocol extensions, &lt;code&gt;Self&lt;/code&gt; (with a capital 'S') refers to the &lt;strong&gt;type that conforms to the protocol&lt;/strong&gt;.&lt;br&gt;
It's like saying, "Whoever adopts this protocol, listen up!"  &lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;protocol Duplicatable {
    func duplicate() -&amp;gt; Self
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the Duplicatable protocol, Self is used to specify that the duplicate() method should return an instance of the conforming type.&lt;br&gt;
The exact type is not specified in the protocol itself, but will be determined by the type that conforms to the protocol.&lt;br&gt;
It allows each conforming type to have a clear contract: if you conform to Duplicatable, you must implement a duplicate() method that returns an instance of your own type.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;Self.self&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;When you use &lt;code&gt;Self.self&lt;/code&gt;, you're referring to the &lt;strong&gt;type itself&lt;/strong&gt; (not an instance).&lt;br&gt;
It's like saying, "I want to talk about the blueprint, not the actual house."&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;class Animal {
    static func describe() {
        print("This is an animal.")
    }
}

class Dog: Animal {
    override class func describe() {
        print("This is a dog.")
    }
}

let someAnimalType = Animal.self
someAnimalType.describe() // Prints: "This is an animal."

let someDogType = Dog.self
someDogType.describe() // Prints: "This is a dog."&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Practical Use Cases&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;self&lt;/code&gt;&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Use it within instance methods to refer to properties or methods of the current instance.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Helpful for avoiding naming conflicts between method parameters and instance properties.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;Self&lt;/code&gt;&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Great for defining default implementations in protocol extensions.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Ensures that conforming types use the same implementation.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;Self.self&lt;/code&gt;&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Rarely used directly but can be handy when working with metatypes (e.g., creating instances dynamically).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;self&lt;/code&gt; is for instances.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Self&lt;/code&gt; is for protocols and protocol extensions.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Self.self&lt;/code&gt; is for talking about types themselves.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Understanding the differences between &lt;code&gt;self&lt;/code&gt;, &lt;code&gt;Self&lt;/code&gt;, and &lt;code&gt;Self.self&lt;/code&gt; is crucial for writing clean and maintainable Swift code.&lt;/p&gt;
&lt;p&gt;Resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://docs.swift.org/swift-book/documentation/the-swift-programming-language/methods/"&gt;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/methods/&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://nilcoalescing.com/blog/UseCasesForSelfInSwift"&gt;https://nilcoalescing.com/blog/UseCasesForSelfInSwift&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Appril Festival 2024</title>    <link>https://wesleydegroot.nl/blog/appril-festival-2024</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/appril-festival-2024</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:50 +0200</pubDate>
    <category>Conference</category>
    <category>Appril Festival</category>
    <category>Appsterdam</category>
    <description>&lt;p&gt;As you may (or may not yet) know, I'd love to support and help the tech industry, today I'd like to highlight Appril Festival, i have volunteered a several times and it's a great event to attend, learn and network.&lt;/p&gt;
&lt;h1&gt;Appril Festival: Celebrating the Intersection of Apps, Design, and Business&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTVrjni_HY6Pa4rZcd5PXNO76DWdmB3ZUWemKnc3xItYQ&amp;amp;s" alt="Appril Festival" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Appril Festival&lt;/strong&gt;, a vibrant celebration at the crossroads of app development, design, and business, is back! From &lt;strong&gt;April 17th to 26th, 2024&lt;/strong&gt;, professionals from all corners of the Netherlands gather for a week filled with inspiration, innovation, and networking. Whether you're a product owner, designer, developer, or business enthusiast, Appril has something exciting in store for you.&lt;/p&gt;
&lt;h2&gt;What Is Appril Festival?&lt;/h2&gt;
&lt;p&gt;Appril Festival is all about &lt;strong&gt;connecting the dots&lt;/strong&gt;—the product side, design aspects, and business elements—involved in creating successful apps. Here's what you can expect during this multi-day extravaganza:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Inspiring Talks&lt;/strong&gt;: Dive into thought-provoking discussions led by industry experts. Learn about mobile businesses, app marketing insights, UX/UI design, and cutting-edge technologies.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Exciting Workshops&lt;/strong&gt;: Participate in hands-on workshops that empower you with practical skills. From behavioral science to impactful presentations, there's something for everyone.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Tasty Lunches&lt;/strong&gt;: Fuel your creativity with delicious lunches while mingling with fellow app enthusiasts.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;My top sessions to look forward to&lt;/h2&gt;
&lt;h3&gt;&lt;a href="https://apprilfestival.com/sessions/the-future-is-artificial/"&gt;The future is Artificial&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Speaker: &lt;a href="https://www.linkedin.com/in/theopalios/"&gt;Theo Palios&lt;/a&gt; from &lt;a href="https://www.linkedin.com/company/unicorn-labs-nl/"&gt;Unicorn Labs&lt;/a&gt;.    &lt;/p&gt;
&lt;p&gt;Dementia is a debilitating disease affecting millions of people worldwide, with Alzheimer’s being the most common form. Unfortunately, many individuals go undiagnosed until the advanced stages, leading to delayed treatment and increased healthcare burdens. Our startup aims to change this by introducing an innovative AI-powered application that detects early warning signals for dementia through behavioral and speech patterns analysis.&lt;/p&gt;
&lt;p&gt;In our talk, we will delve into the problem of late diagnosis in dementia patients, highlighting its impact on individuals, families, and healthcare systems. We will also discuss the limitations of current diagnostic methods and how AI can address these challenges. Our solution involves three neural networks that analyze speech patterns to detect context, speed, and word forgetfulness, providing early warnings for Alzheimer’s and dementia.&lt;/p&gt;
&lt;p&gt;We will share our preliminary results, demonstrating the effectiveness of our application in identifying individuals at risk of developing dementia up to five years before symptoms manifest. Our solution also provides personalized care recommendations based on individual needs, improving quality of life and reducing healthcare costs.&lt;/p&gt;
&lt;p&gt;Join us as we explore the future of dementia diagnosis and treatment through AI-powered solutions. Together, let’s work towards a world where early intervention becomes the norm, rather than the exception.”&lt;/p&gt;
&lt;h3&gt;&lt;a href="https://apprilfestival.com/sessions/weekend-fun/"&gt;Weekend Fun: Scheepvaartmuseum (Maritime Museum)&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;By &lt;a href="https://www.linkedin.com/in/0xWDG/"&gt;Wesley de Groot&lt;/a&gt; &amp;amp; &lt;a href="https://www.linkedin.com/in/jakeruston/"&gt;Jake Ruston&lt;/a&gt;, a &lt;a href="https://appsterdam.rs/"&gt;Appsterdam&lt;/a&gt; Event.  &lt;/p&gt;
&lt;p&gt;Het Scheepvaartmuseum (formerly Nederlands Scheepvaartmuseum Amsterdam) is a Dutch museum at Kattenburgerplein 1 in Amsterdam about shipping and maritime history. It is housed in the building of the former ‘s Lands Zeemagazijn and contains the second largest maritime collection in the world. It specializes in the maritime history of the Netherlands.&lt;/p&gt;
&lt;h3&gt;&lt;a href="https://apprilfestival.com/sessions/appsterdam-pinch-digital-sustainability/"&gt;Sustainable solutions for our digital dependency&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Speaker: &lt;a href="https://www.linkedin.com/in/roelandweve/"&gt;Roeland Weve&lt;/a&gt; from &lt;a href="https://pinch.nl/"&gt;Pinch&lt;/a&gt;.  &lt;/p&gt;
&lt;p&gt;Cloud computing does not take place in the sky.&lt;br&gt;
Gigantic data centers are popping up all around us, home to new AI services and BitCoin mining operations that consume more energy than the entire country of the Netherlands annually.&lt;br&gt;
Code using Python &amp;amp; Java (the most popular) can use up to 45x times as much energy as other programming languages.&lt;br&gt;
Whether you are a programmer or user, our dependence on all this processing power is creating a new energy competition between our homes / businesses and our digital lives.&lt;br&gt;
What are some examples for sustainable development? Join us April 18th where we talk to industry pioneers that are developing serious solutions to solve our addiction to energy. &lt;/p&gt;
&lt;h3&gt;&lt;a href="https://apprilfestival.com/sessions/midjourney-basics-2-hours-into-midjourney/"&gt;Midjourney Basics: 2-hours into Midjourney&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Speaker: &lt;a href="https://www.linkedin.com/in/roelandweve/"&gt;Roeland Weve&lt;/a&gt; from &lt;a href="https://pinch.nl/"&gt;Pinch&lt;/a&gt;.  &lt;/p&gt;
&lt;p&gt;In this two-hour workshop, we will lead you through the fundamentals of Midjourney, offering real-time insights into crafting beautiful artwork for various purposes, including mobile apps, websites, promotional materials, and more.&lt;br&gt;
Feel encouraged to ask any questions you may have throughout the session.&lt;br&gt;
By the workshop’s end, you’ll possess the skills necessary to generate impressive images effortlessly, simply by composing straightforward text prompts.&lt;/p&gt;
&lt;h3&gt;&lt;a href="https://apprilfestival.com/sessions/rapid-app-development-with-flutterflow/"&gt;Rapid app development with FlutterFlow&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Speaker: &lt;a href="https://www.linkedin.com/in/segveenstra/"&gt;Stephan Veenstra&lt;/a&gt; &amp;amp; &lt;a href="https://www.linkedin.com/in/stephan-mantel-18528455/"&gt;Stephan Mantel&lt;/a&gt; from &lt;a href="https://pinch.nl/"&gt;Pinch&lt;/a&gt;.  &lt;/p&gt;
&lt;p&gt;None of the code, all of the glory&lt;/p&gt;
&lt;p&gt;Full agenda:&lt;br&gt;
&lt;a href="https://apprilfestival.com/category/festival/2024/"&gt;https://apprilfestival.com/category/festival/2024/&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="https://apprilfestival.com/agencies/"&gt;Discover the (Dutch) Industry&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Join Us!&lt;/h2&gt;
&lt;p&gt;Appril Festival is more than an event; it's a community. Whether you're a seasoned pro or just starting your app journey, there's a place for you. Mark your calendars, embrace the spirit of innovation, and let's celebrate the magic of apps together!&lt;/p&gt;
&lt;p&gt;For more details and registration, visit the &lt;a href="https://apprilfestival.com/"&gt;official Appril Festival website&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://appsterdam.rs"&gt;Appsterdam&lt;/a&gt; is partnering with Appril Festival, and we are looking forward to seeing you at one of the many events!&lt;/p&gt;</description>
  </item>
  <item>
    <title>If case let</title>    <link>https://wesleydegroot.nl/blog/if-case-let</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/if-case-let</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:50 +0200</pubDate>
    <category>Swift</category>
    <description>&lt;p&gt;This concise syntax provides a convenient shortcut when you want to match a single case without writing a full-blown &lt;code&gt;switch&lt;/code&gt; statement. Although it might seem a bit backward at first, once you grasp its power, you'll find it quite handy.&lt;/p&gt;
&lt;h3&gt;The Scenario&lt;/h3&gt;
&lt;p&gt;Imagine you're working with the Swift &lt;code&gt;Result&lt;/code&gt; type, capturing the results of a throwing expression (such as loading data from a file). Here's how you'd typically handle it:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import Foundation

let result = Result {
    try Data(
        contentsOf: URL(
            string: "https://wesleydegroot.nl"
        )!
    )
}

switch result {
case .success(let data):
    // Do something with the data
    print(data)
case .failure(let error):
    // Handle the error
    print(error)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;The Syntax&lt;/h3&gt;
&lt;p&gt;Now, let's introduce the &lt;code&gt;if case let&lt;/code&gt; syntax. It allows you to directly match a specific case without the need for a full &lt;code&gt;switch&lt;/code&gt; statement:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;if case let .failure(error) = result {
    // Handle the error
    print(error)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alternatively, you can use the slightly more concise form:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;if case .failure(let error) = result {
    // Handle the error
    print(error)
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Adding Conditions&lt;/h3&gt;
&lt;p&gt;You can further filter the matching cases by adding a &lt;code&gt;where&lt;/code&gt; clause. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;if case let .success(data) = result, data.count &amp;gt; 100 {
    // Process the data (only if its count exceeds 100)
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Beyond Switch Statements&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;case let&lt;/code&gt; syntax isn't restricted to &lt;code&gt;switch&lt;/code&gt; statements. You can also use it in &lt;code&gt;if&lt;/code&gt; statements or even &lt;code&gt;guard&lt;/code&gt; statements:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;// Using an if statement
if case let .success(data) = result {
    // Handle the success case
}

// Using an alternate form
if case .success(let data) = result {
    // Handle the success case
}

// Using a guard statement
guard case let .success(data) = result, data.count &amp;gt; 100 else {
    return
}

// Continue with further processing&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="https://wesleydegroot.nl/resources/if-case-let.swiftpm.zip"&gt;Download Swift Playground&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;In this post, you learned about the &lt;code&gt;if case let&lt;/code&gt; syntax in Swift, which provides a concise way to match a specific case without the need for a full &lt;code&gt;switch&lt;/code&gt; statement. This syntax is particularly useful when you're working with types like &lt;code&gt;Result&lt;/code&gt; and want to handle the success or failure cases separately. By using &lt;code&gt;if case let&lt;/code&gt;, you can streamline your code and make it more readable.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;if case let&lt;/code&gt; syntax in Swift is a powerful feature that allows you to match specific cases without the need for a full &lt;code&gt;switch&lt;/code&gt; statement. This concise syntax is particularly useful when you're working with types like &lt;code&gt;Result&lt;/code&gt; and want to handle the success or failure cases separately. By using &lt;code&gt;if case let&lt;/code&gt;, you can streamline your code and make it more readable. Give it a try in your next project and see how it can help you write cleaner, more maintainable code.&lt;/p&gt;</description>
  </item>
  <item>
    <title>Variadic Parameters</title>    <link>https://wesleydegroot.nl/blog/variadic-parameters</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/variadic-parameters</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:51 +0200</pubDate>
    <category>Swift</category>
    <description>&lt;p&gt;These versatile constructs allow us to handle a variable number of arguments of the same type within a function.&lt;/p&gt;
&lt;h2&gt;What are &lt;code&gt;Variadic Parameters&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;In Swift, &lt;strong&gt;variadic parameters&lt;/strong&gt; enable a function to accept zero or more values of a specific type.&lt;br&gt;
They come in handy when you want to create a function that can take any number of arguments without specifying the exact count upfront.&lt;br&gt;
Imagine a scenario where you're dealing with a method that often works with individual elements, and you'd rather avoid creating an array just for a single value at the implementation level.&lt;/p&gt;
&lt;h3&gt;How to Define a Variadic Parameter&lt;/h3&gt;
&lt;p&gt;To declare a variadic parameter, use a value type followed by three dots &lt;code&gt;...&lt;/code&gt;.&lt;br&gt;
&lt;br&gt;&lt;br&gt;
Here's an example:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;mutating func add(_ newContent: Content...) {
    content.append(contentsOf: newContent)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this snippet:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;add(_:)&lt;/code&gt; is a method that can accept either a single value or multiple values at once.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The &lt;code&gt;Content&lt;/code&gt; type represents the elements we're adding to our collection.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can now call this method with one or more &lt;code&gt;Content&lt;/code&gt; instances:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;var posts = BlogPosts()

// Add a single content item:
posts.add(Content(title: "Blog post 1"))

// Add multiple items:
posts.add(
    Content(title: "Blog post 2"), 
    Content(title: "Blog post 3")
)&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Handling Multiple Variadic Arguments&lt;/h3&gt;
&lt;p&gt;Since Swift 5.4 (shipped with Xcode 12.5), you can define multiple variadic arguments in a method:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;mutating func add(_ newContent: Content..., newUsers: User...) {
    content.append(contentsOf: newContent)
    users.append(contentsOf: newUsers)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This flexibility allows multiple parameters to receive zero or more values.&lt;/p&gt;
&lt;h2&gt;Caveats / Limitations&lt;/h2&gt;
&lt;p&gt;While variadic parameters are powerful, they have some limitations to consider:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Empty Variadic Arguments&lt;/strong&gt;: You can pass an empty array as a variadic argument. For instance:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct BlogPosts {
    private(set) var content: [Content] = []

    mutating func add(_ newContent: [Content]) {
        content.append(contentsOf: newContent)
    }
}

var posts = BlogPosts()
    posts.add([])&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The same applies when using variadic parameters directly:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct BlogPosts {
    private(set) var content: [Content] = []

    mutating func add(_ newContent: Content...) {
        content.append(contentsOf: newContent)
    }
}

var posts = BlogPosts()
    posts.add()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Type Consistency&lt;/strong&gt;: Variadic parameters must have the same type. You cannot mix different types within a single variadic argument.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Variadic parameters are a powerful feature in Swift that allows you to create functions that can accept a variable number of arguments.&lt;/p&gt;
&lt;p&gt;Resources:&lt;br&gt;
&lt;a href="https://docs.swift.org/swift-book/documentation/the-swift-programming-language/functions/"&gt;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/functions/&lt;/a&gt;&lt;/p&gt;</description>
  </item>
  <item>
    <title>Generics in Swift</title>    <link>https://wesleydegroot.nl/blog/swift-generics</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/swift-generics</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:51 +0200</pubDate>
    <category>Swift</category>
    <category>Generics</category>
    <description>&lt;p&gt;Swift, offers a powerful feature called &lt;strong&gt;generics&lt;/strong&gt; that greatly enhances code reusability, efficiency, and safety. In this blog post, we will dive deep into generics and explore how they can be leveraged in iOS development. Let's get started!&lt;/p&gt;
&lt;h2&gt;What are Generics?&lt;/h2&gt;
&lt;p&gt;Generics in Swift enable you to write flexible and reusable code that can work with different types of data. By using generics, you can create functions, classes, and structures that operate uniformly on a variety of types, avoiding code duplication and increasing maintainability.&lt;/p&gt;
&lt;h2&gt;Generic Types&lt;/h2&gt;
&lt;p&gt;A generic type can represent any specific type, allowing for maximum flexibility. Let's look at an example of a generic class called &lt;code&gt;Stack&lt;/code&gt; that can store and manipulate a stack of elements of any type:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;class Stack&amp;lt;T&amp;gt; {
    private var items = [T]()

    func push(item: T) {
        items.append(item)
    }

    func pop() -&amp;gt; T? {
        return items.popLast()
    }
}

let stackOfIntegers = Stack&amp;lt;Int&amp;gt;()
let stackOfString = Stack&amp;lt;String&amp;gt;()
let stackOfPerson = Stack&amp;lt;Person&amp;gt;()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the code snippet above, we define a &lt;code&gt;Stack&lt;/code&gt; class with a generic type parameter &lt;code&gt;T&lt;/code&gt;. This parameter acts as a placeholder for any type that will be used with the &lt;code&gt;Stack&lt;/code&gt; instance. The &lt;code&gt;push&lt;/code&gt; function allows us to add elements to the stack, while the &lt;code&gt;pop&lt;/code&gt; function removes and returns the topmost element from the stack.&lt;/p&gt;
&lt;h2&gt;Generic Functions&lt;/h2&gt;
&lt;p&gt;Similarly, you can define generic functions that can work with different types. Let's look at an example of a generic function for swapping two values:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;func swap&amp;lt;T&amp;gt;(_ a: inout T, _ b: inout T) {
    let temp = a
    a = b
    b = temp
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this code snippet, the &lt;code&gt;swap&lt;/code&gt; function is defined with a type parameter &lt;code&gt;T&lt;/code&gt; using the placeholder &lt;code&gt;&amp;lt;T&amp;gt;&lt;/code&gt;. The function takes in two parameters of the same type (&lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt;) and swaps their values using a temporary variable.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Generics in Swift are a powerful feature that allows you to write flexible and reusable code that can work with different types. By using generics, you can avoid code duplication, increase maintainability, and improve efficiency in your iOS development projects. I hope this blog post has helped you understand the basics of generics in Swift and how you can leverage them in your code.&lt;/p&gt;
&lt;p&gt;Resources:&lt;br&gt;
&lt;a href="https://docs.swift.org/swift-book/documentation/the-swift-programming-language/generics/"&gt;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/generics/&lt;/a&gt;&lt;/p&gt;</description>
  </item>
  <item>
    <title>Handle hyperlinks in SwiftUI</title>    <link>https://wesleydegroot.nl/blog/handle-hyperlinks-in-swiftui</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/handle-hyperlinks-in-swiftui</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:52 +0200</pubDate>
    <category>SwiftUI</category>
    <category>Hyperlink</category>
    <description>&lt;p&gt;If you have a SwiftUI application and you present text with a URL, have you notices how the URL is being opened by Safari and not an in-app browser, and want to change that?&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;OpenURLAction&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;An action that opens a URL.&lt;br&gt;
OpenURLAction can be used to create your own handler for opening url's in SwiftUI, this makes it possible to use your own (in-app) WebView or transform the url before you open it in a external browser.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;OpenURLAction&lt;/code&gt; requires a &lt;code&gt;return&lt;/code&gt; statement with an appropiate value, in the (psuedo) codeblock below you can see which results are possible and what their meaning is.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct OpenURLAction.Result {
    /// The handler did handle the URL.
    let handled

    /// The handler discarded the URL.
    let discarded

    /// The handler asks the system to open the original URL.
    let systemAction

    /// The handler asks the system to open the modified URL.
    let systemAction(let URL)
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Use Case: Simple url handler&lt;/h2&gt;
&lt;p&gt;requires you to have a function called &lt;code&gt;handleURL()&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;/// Basic example
Text("Visit [my Blog](https://www.wesleydegroot.nl) for more details.")
    .environment(\.openURL, OpenURLAction { url in
        handleURL(url) // Define this method to take appropriate action.
        return .handled
    })&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Use Case: Simple url handler&lt;/h2&gt;
&lt;p&gt;requires you to have a function called &lt;code&gt;handleURL()&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct SafariURL: Identifiable {
    let id = UUID()
    let url: URL
}

struct ContentView: View {
    @State
    var safariURL: SafariURL?

    var body: some View {
        Text("Visit [my Blog](https://www.wesleydegroot.nl) for more details.")
            .environment(\.openURL, OpenURLAction { url in
                safariURL = SafariURL(url: url)
                return .handled
            })
            .sheet(item: $safariURL, content: { safariURL in
                SafariView(url: safariURL.url)
            })
    }

    func handleURL(url: URL) {
    /// open SFSafariViewController
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;OpenURLAction&lt;/code&gt; is a great way to handle URL's in SwiftUI, it allows you to create your own handler for URL's and decide what to do with them.&lt;/p&gt;
&lt;p&gt;Resources:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/openurlaction"&gt;https://developer.apple.com/documentation/swiftui/openurlaction&lt;/a&gt;&lt;/p&gt;</description>
  </item>
  <item>
    <title>Loging using OSLog</title>    <link>https://wesleydegroot.nl/blog/oslog</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/oslog</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:52 +0200</pubDate>
    <category>Swift</category>
    <category>OSLog</category>
    <description>&lt;p&gt;OSLog is a Swift API that provides a unified logging system for all Apple platforms.&lt;br&gt;
It is a replacement for the older &lt;code&gt;print&lt;/code&gt; and &lt;code&gt;NSLog&lt;/code&gt; functions.&lt;br&gt;
OSLog is a more efficient and secure logging system that provides better performance and privacy protection.&lt;br&gt;
In this article, we will explore the features of OSLog and how to use it in your Swift applications.&lt;/p&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;p&gt;To use &lt;code&gt;OSLog&lt;/code&gt; in your Swift application, you need to import the &lt;code&gt;OSLog&lt;/code&gt; module.&lt;br&gt;
You can do this by adding the following import statement to your Swift file:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import OSLog&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then you can use the &lt;code&gt;Logger&lt;/code&gt; class to create a logger instance.&lt;br&gt;
The &lt;code&gt;Logger&lt;/code&gt; class is a wrapper around the &lt;code&gt;OSLog&lt;/code&gt; API that provides a more convenient way to log messages.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;let logger = Logger(
    subsystem: "nl.wesleydegroot.demoApp",
    category: "myCategory"
)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;subsystem&lt;/code&gt; parameter is a string that identifies the subsystem that the logger belongs to.&lt;br&gt;
The &lt;code&gt;category&lt;/code&gt; parameter is a string that identifies the category of the logger.&lt;/p&gt;
&lt;h2&gt;Sample Use&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import OSLog

let logger = Logger(
    subsystem: "nl.wesleydegroot.demoApp",
    category: "myCategory"
)

logger.fault("This is a fault") // Shows in red
logger.error("This is a error") // Shows in yellow
logger.warning("This is a warning") // Shows in yellow
logger.info("Information") // Shows in default color
logger.debug("Debug message") // Shows in default color
logger.trace("Trace message") // Shows in default color&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;p&gt;Unfortunately, &lt;code&gt;OSLog&lt;/code&gt; is only available on Apple platforms and it cannot be used within SwiftUI views.&lt;br&gt;
If you need to log messages within a SwiftUI view, use the view extension below:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

extension View {
    func log(_ closure: () -&amp;gt; Void) -&amp;gt; some View {
         _ = closure()
         return self
     }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you can log messages within a SwiftUI view like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI
import OSLog

struct ContentView: View {
    let logger = Logger(
        subsystem: "nl.wesleydegroot.exampleapp",
        category: "MyCategory"
    )

    var body: some View {
        Text("Hello, World!")
            .log {
                logger.info("Hello, World!")
            }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Displaying Log Messages (in app)&lt;/h2&gt;
&lt;p&gt;If you want to display log messages in your app, you can use the &lt;code&gt;OSLog&lt;/code&gt; API to log messages to the console.&lt;br&gt;
I've created a Swift Package to make this easier, you can find it &lt;a href="https://github.com/0xWDG/OSLogViewer"&gt;here (OSLogViewer)&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;How to use &lt;code&gt;OSLogViewer&lt;/code&gt;:&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI
import OSLogViewer

struct ContentView: View {
    var body: some View {
        NavigationView {
            // The default configuration will show the log messages.
            OSLogViewer()

            // Custom configuration
            // OSLogViewer(
            //    subsystem: "nl.wesleydegroot.exampleapp",
            //    since: Date().addingTimeInterval(-7200) // 2 hours
            // )
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="https://wesleydegroot.nl/resources/OSLog.swiftpm.zip"&gt;Download Swift Playground&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;OSLogViewer Features&lt;/h3&gt;
&lt;h4&gt;Simple Interface&lt;/h4&gt;
&lt;p&gt;&lt;img src="../resources/OSLogViewer.png" alt="OSLog Viewer" loading="lazy"&gt;&lt;/p&gt;
&lt;h4&gt;Beautiful export&lt;/h4&gt;
&lt;pre&gt;&lt;code class="language-log"&gt;This is the OSLog archive for exampleapp
Generated on 2/6/2024, 11:53
Generator https://github.com/0xWDG/OSLogViewer

Info message
ℹ️ 2/6/2024, 11:53 🏛️ exampleapp ⚙️ nl.wesleydegroot.exampleapp 🌐 myCategory

Error message
❗ 2/6/2024, 11:53 🏛️ exampleapp ⚙️ nl.wesleydegroot.exampleapp 🌐 myCategory

Error message
❗ 2/6/2024, 11:53 🏛️ exampleapp ⚙️ nl.wesleydegroot.exampleapp 🌐 myCategory

Critical message
‼️ 2/6/2024, 11:53 🏛️ exampleapp ⚙️ nl.wesleydegroot.exampleapp 🌐 myCategory

Log message
🔔 2/6/2024, 11:53 🏛️ exampleapp ⚙️ nl.wesleydegroot.exampleapp 🌐 myCategory

Log message
🔔 2/6/2024, 11:53 🏛️ exampleapp ⚙️ nl.wesleydegroot.exampleapp 🌐 myCategory&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;OSLog is a more efficient and secure logging system that provides better performance and privacy protection.&lt;br&gt;
It is a great replacement for the older &lt;code&gt;print&lt;/code&gt; and &lt;code&gt;NSLog&lt;/code&gt; functions, that provides a unified logging system for all Apple platforms.&lt;/p&gt;
&lt;p&gt;Resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/0xWDG/OSLogViewer"&gt;https://github.com/0xWDG/OSLogViewer&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/os/logging"&gt;https://developer.apple.com/documentation/os/logging&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>@Environment variables</title>    <link>https://wesleydegroot.nl/blog/@environment</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/@environment</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:53 +0200</pubDate>
    <category>Environment</category>
    <category>SwiftUI</category>
    <description>&lt;p&gt;SwiftUI provides a way to pass data down the view hierarchy using &lt;code&gt;@Environment&lt;/code&gt; variables. These variables are environment-dependent and can be accessed from any child view. They are useful for sharing common data or settings across the app, such as color schemes, locale, or accessibility settings.&lt;/p&gt;
&lt;p&gt;When you create an &lt;code&gt;@Environment&lt;/code&gt; variable, SwiftUI automatically manages its value for you. This means that when the environment changes, the view will automatically update to reflect the new value.&lt;/p&gt;
&lt;h2&gt;Creating an @Environment Variable&lt;/h2&gt;
&lt;p&gt;To create an &lt;code&gt;@Environment&lt;/code&gt; variable, you can use one of the built-in environment keys provided by SwiftUI, such as &lt;code&gt;.colorScheme&lt;/code&gt;, &lt;code&gt;.locale&lt;/code&gt;, or &lt;code&gt;.accessibilityEnabled&lt;/code&gt;. For example, to access the color scheme environment variable, you can use the following code:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct ContentView: View {
    @Environment(\.colorScheme) var colorScheme

    var body: some View {
        Text("Hello, World!")
            .foregroundColor(colorScheme == .dark ? .white : .black)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, the &lt;code&gt;colorScheme&lt;/code&gt; variable is an &lt;code&gt;@Environment&lt;/code&gt; variable that holds the current color scheme of the app. The text color is set based on the color scheme (dark or light) using a ternary operator.&lt;/p&gt;
&lt;h2&gt;Custom @Environment Variables&lt;/h2&gt;
&lt;p&gt;You can also create custom &lt;code&gt;@Environment&lt;/code&gt; variables to pass custom data or settings down the view hierarchy. To define a custom &lt;code&gt;@Environment&lt;/code&gt; variable, you need to create a new type that conforms to the &lt;code&gt;EnvironmentKey&lt;/code&gt; protocol and extend &lt;code&gt;EnvironmentValues&lt;/code&gt; to introduce the value.&lt;/p&gt;
&lt;p&gt;Here's an example of how you can define a custom &lt;code&gt;@Environment&lt;/code&gt; variable:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;private struct MyCustomKey: EnvironmentKey {
    // Create a default value for the custom environment key
    static let defaultValue: String = "Default Value"
}

extension EnvironmentValues {
    /// Define a custom environment variable
    var myCustomValue: String {
        get { self[MyCustomKey.self] }
        set { self[MyCustomKey.self] = newValue }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, we define a custom key &lt;code&gt;MyCustomKey&lt;/code&gt; that holds a default string value. We then extend &lt;code&gt;EnvironmentValues&lt;/code&gt; to introduce the custom value &lt;code&gt;myCustomValue&lt;/code&gt; using the key.&lt;/p&gt;
&lt;p&gt;You can now use the custom &lt;code&gt;@Environment&lt;/code&gt; variable in your views like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;/// Pass custom environment value
struct ContentView: View {
    var body: some View {
        VStack {
            SecondView()
                .environment(\.myCustomValue, "hello there")
        }
    }
}

/// Access custom environment value
struct SecondView: View {
    @Environment(\.myCustomValue) var customValue

    var body: some View {
        Text(customValue)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, the &lt;code&gt;customValue&lt;/code&gt; variable is an &lt;code&gt;@Environment&lt;/code&gt; variable that holds the custom value defined earlier. The text view displays the value of the custom environment variable.&lt;/p&gt;
&lt;h2&gt;iOS 18 and later (easier)&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;/// Setup environment key
extension EnvironmentValues {
    @Entry var myCustomValue: String = "Default Value"
}

/// Pass custom environment value
struct ContentView: View {
    var body: some View {
        VStack {
            SecondView()
                .environment(\.myCustomValue, "hello there")
        }
    }
}

/// Access custom environment value
struct SecondView: View {
    @Environment(\.myCustomValue) var customValue

    var body: some View {
        Text(customValue)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;p&gt;When using &lt;code&gt;@Environment&lt;/code&gt; variables, keep in mind that they are not meant for sharing complex data structures or models across the view hierarchy. For more complex data sharing, consider using &lt;code&gt;@EnvironmentObject&lt;/code&gt; or other data sharing techniques.&lt;/p&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;@Environment variables are a powerful tool in SwiftUI for sharing common data or settings across the app. By creating custom &lt;code&gt;@Environment&lt;/code&gt; variables, you can extend this functionality to suit your app's specific needs. Just remember to use them judiciously and follow best practices to avoid potential pitfalls.&lt;/p&gt;
&lt;p&gt;Resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/environment"&gt;SwiftUI Environment&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>@ViewBuilder in Swift</title>    <link>https://wesleydegroot.nl/blog/@viewbuilder</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/@viewbuilder</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:53 +0200</pubDate>
    <category>ViewBuilder</category>
    <category>Swift</category>
    <description>&lt;p&gt;SwiftUI, Apple's declarative framework for building user interfaces, introduces several powerful tools to streamline UI development. One such tool is the &lt;code&gt;@ViewBuilder&lt;/code&gt; attribute, which simplifies the creation of complex view hierarchies. In this article, we'll explore what &lt;code&gt;@ViewBuilder&lt;/code&gt; is, how it works, and how you can use it to enhance your SwiftUI projects.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;@ViewBuilder&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;@ViewBuilder&lt;/code&gt; is a result builder provided by SwiftUI that allows you to construct views from closures. It enables you to write more readable and maintainable code by eliminating the need for explicit return statements in your view-building closures.&lt;/p&gt;
&lt;h3&gt;How &lt;code&gt;@ViewBuilder&lt;/code&gt; Works&lt;/h3&gt;
&lt;p&gt;When you use &lt;code&gt;@ViewBuilder&lt;/code&gt;, you can combine multiple views within a single closure without needing to return each view explicitly. This is particularly useful for creating complex layouts with conditional views.&lt;/p&gt;
&lt;p&gt;Here's a simple example:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            Text("Hello, world!")
                .padding()
            if Bool.random() {
                Text("This is a conditional view")
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, the &lt;code&gt;VStack&lt;/code&gt; contains two &lt;code&gt;Text&lt;/code&gt; views, one of which is conditionally included based on a random boolean value. The &lt;code&gt;@ViewBuilder&lt;/code&gt; attribute allows this structure to be written cleanly and concisely.&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;@ViewBuilder&lt;/code&gt; in Custom Views&lt;/p&gt;
&lt;p&gt;You can also use &lt;code&gt;@ViewBuilder&lt;/code&gt; in your custom view initializers and methods. This allows you to create reusable components that accept multiple child views.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct CustomContainer&amp;lt;Content: View&amp;gt;: View {
    let content: Content

    init(@ViewBuilder content: () -&amp;gt; Content) {
        self.content = content()
    }

    var body: some View {
        VStack {
            content
        }
    }
}

struct ContentView: View {
    var body: some View {
        CustomContainer {
            Text("Hello, world!")
            Text("This is inside a custom container")
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, &lt;code&gt;CustomContainer&lt;/code&gt; is a reusable component that accepts a closure containing multiple child views. The &lt;code&gt;@ViewBuilder&lt;/code&gt; attribute makes it easy to pass these views without needing to wrap them in a single container view.&lt;/p&gt;
&lt;h3&gt;Benefits of Using &lt;code&gt;@ViewBuilder&lt;/code&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Improved Readability&lt;/strong&gt;: By removing the need for explicit return statements, &lt;code&gt;@ViewBuilder&lt;/code&gt; makes your code cleaner and easier to read.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Conditional Views&lt;/strong&gt;: You can easily include or exclude views based on conditions, making your UI more dynamic.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Reusable Components&lt;/strong&gt;: Create flexible and reusable components that can accept multiple child views.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;p&gt;While &lt;code&gt;@ViewBuilder&lt;/code&gt; is a powerful tool, there are a few things to keep in mind when using it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Limitations&lt;/strong&gt;: &lt;code&gt;@ViewBuilder&lt;/code&gt; can only be used with functions that return a single view. If you need to return multiple views, you'll need to use a different approach.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Performance&lt;/strong&gt;: While &lt;code&gt;@ViewBuilder&lt;/code&gt; can improve code readability, it may not always be the most performant option. Be mindful of how you use it in performance-critical sections of your code.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Complexity&lt;/strong&gt;: Overusing &lt;code&gt;@ViewBuilder&lt;/code&gt; can lead to overly complex view hierarchies. Use it judiciously to maintain code clarity.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Debugging&lt;/strong&gt;: When debugging views that use &lt;code&gt;@ViewBuilder&lt;/code&gt;, keep in mind that the closure-based nature of &lt;code&gt;@ViewBuilder&lt;/code&gt; can make it harder to trace the source of issues.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;@ViewBuilder&lt;/code&gt; attribute is a powerful tool in SwiftUI that simplifies the process of building complex view hierarchies. By leveraging &lt;code&gt;@ViewBuilder&lt;/code&gt;, you can write more readable, maintainable, and dynamic SwiftUI code. Whether you're building simple layouts or intricate interfaces, &lt;code&gt;@ViewBuilder&lt;/code&gt; can help you achieve your goals with ease.&lt;/p&gt;
&lt;p&gt;Resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://www.avanderlee.com/swiftui/viewbuilder/"&gt;SwiftLee - @ViewBuilder usage explained with code examples&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/viewbuilder"&gt;Apple Developer Documentation - ViewBuilder&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>CoreSpotlight</title>    <link>https://wesleydegroot.nl/blog/corespotlight</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/corespotlight</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:54 +0200</pubDate>
    <category>CoreSpotlight</category>
    <category>Swift</category>
    <description>&lt;p&gt;CoreSpotlight is a framework provided by Apple that allows you to index and search content within your app. It provides a way to make your app's content searchable and discoverable by users through the Spotlight search feature on iOS and macOS.&lt;/p&gt;
&lt;h2&gt;Why should i implement &lt;code&gt;CoreSpotlight&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;Implementing CoreSpotlight in your app can greatly enhance the discoverability and searchability of your app's content. By indexing your app's content using CoreSpotlight, users will be able to find relevant information from your app directly through the Spotlight search feature on iOS and macOS.&lt;/p&gt;
&lt;p&gt;This can lead to a better user experience, as users can quickly access specific content within your app without having to open the app itself. It also allows your app to be more integrated with the overall system search functionality, making it easier for users to find and interact with your app's content.&lt;/p&gt;
&lt;p&gt;Additionally, CoreSpotlight provides a powerful set of APIs and features that allow you to customize the search experience and provide rich metadata for your app's content. You can define searchable attributes, create custom search UI, and even deep link users to specific content within your app.&lt;/p&gt;
&lt;p&gt;Overall, implementing CoreSpotlight can help increase the visibility and accessibility of your app's content, improving user engagement and satisfaction.&lt;/p&gt;
&lt;h2&gt;How to implement &lt;code&gt;CoreSpotlight&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;To implement CoreSpotlight in your app, you need to follow these steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Import CoreSpotlight&lt;/strong&gt;: First, import the CoreSpotlight framework in your project.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Indexing Content&lt;/strong&gt;: Identify the content within your app that you want to make searchable and index it using the CoreSpotlight APIs. You can create &lt;code&gt;CSSearchableItem&lt;/code&gt; objects to represent the content and define searchable attributes such as title, description, keywords, and more.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Updating the Index&lt;/strong&gt;: Whenever the content in your app changes, update the CoreSpotlight index to reflect the changes. You can add, update, or delete searchable items using the &lt;code&gt;CSSearchableIndex&lt;/code&gt; API.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Handling Search Queries&lt;/strong&gt;: Implement the search functionality in your app to handle search queries from the user. You can use the &lt;code&gt;CSSearchQuery&lt;/code&gt; API to perform searches and retrieve search results based on the user's input.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Customizing Search Results&lt;/strong&gt;: Customize the search results to provide a rich and interactive search experience for users. You can display custom UI elements, provide additional metadata, and deep link users to specific content within your app.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;By following these steps, you can effectively implement CoreSpotlight in your app and make your content searchable and discoverable through the Spotlight search feature.&lt;/p&gt;
&lt;h2&gt;Example of Use Case&lt;/h2&gt;
&lt;p&gt;Here's an example of how you can implement CoreSpotlight in a simple pokedex app:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import CoreSpotlight

// Define a Pokemon struct
struct Pokemon {
    let id: String
    let name: String
    let description: String
}

// Index a pokemon
func indexPokemon(pokemon: Pokemon) {
    let attributeSet = CSSearchableItemAttributeSet(
        contentType: .content
    )
    attributeSet.title = pokemon.name
    attributeSet.contentDescription = pokemon.description
    attributeSet.keywords = ["Pokedex", pokemon.id, pokemon.name, pokemon.description]

    // Optionally set thumbnail image using the options below
    // attributeSet.thumbnailData
    // attributeSet.thumbnailURL
    // attributeSet.darkThumbnailURL

    let searchableItem = CSSearchableItem(
        uniqueIdentifier: pokemon.id,
        domainIdentifier: "nl.wesleydegroot.pokedex",
        attributeSet: attributeSet
    )

    CSSearchableIndex.default().indexSearchableItems([searchableItem]) { error in
        if let error = error {
            print("Error indexing pokemon: \(error)")
        } else {
            print("Pokemon \(pokemon.name) indexed successfully")
        }
    }
}

// Update the pokemon
func updatePokemon(pokemon: Pokemon) {
    // Update the pokemon in the app
    // ...

    // Re-index the pokemon
    indexPokemon(pokemon: pokemon)
}

// Delete a Pokemon
func deletePokemon(pokemon: String) {
    CSSearchableIndex.default().deleteSearchableItems(withIdentifiers: [pokemon]) { error in
        if let error = error {
            print("Error deleting Pokemon: \(error)")
        } else {
            print("Pokemon deleted successfully")
        }
    }
}

let pokemon: [Pokemon] = [
    Pokemon(
        id: "149",
        name: "Dragonite",
        description: "It is said that somewhere in the ocean lies an island where these gather. Only they live there."
    ),
    Pokemon(
        id: "25",
        name: "Pikachu",
        description: "When it is angered, it immediately discharges the energy stored in the pouches in its cheeks."
    ),
    Pokemon(
        id: "249",
        name: "Lugia",
        description: "Lugia’s wings pack devastating power—a light fluttering of its wings can blow apart regular houses. As a result, this Pokémon chooses to live out of sight deep under the sea."
    )
]


// Index the pokemon
pokemon.forEach { indexPokemon(pokemon: $0) }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, we define a &lt;code&gt;Pokemon&lt;/code&gt; struct representing a pokemon in the app. We then implement functions to index, update, and delete pokemon using CoreSpotlight. When a pokemon is indexed, it becomes searchable through the Spotlight search feature, allowing users to find it based on the title and content.&lt;/p&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;p&gt;When implementing CoreSpotlight in your app, there are a few things to keep in mind:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Privacy Considerations&lt;/strong&gt;: Ensure that you handle user data and content responsibly when indexing it with CoreSpotlight. Be mindful of user privacy and only index content that users have consented to make searchable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Indexing Performance&lt;/strong&gt;: Indexing large amounts of content or frequently updating the index can impact the performance of your app. Consider batching updates and indexing only relevant content to optimize performance.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Search Relevance&lt;/strong&gt;: To provide a good search experience for users, make sure that the searchable attributes of your content are relevant and accurately reflect the content. Use keywords, descriptions, and other metadata to improve search results.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;User Experience&lt;/strong&gt;: Design the search UI and search results to provide a seamless and intuitive experience for users. Consider customizing the search results to display additional information and make it easy for users to interact with the content.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;CoreSpotlight is a powerful framework that allows you to make your app's content searchable and discoverable through the Spotlight search feature on iOS and macOS. By indexing your app's content and handling search queries effectively, you can enhance the user experience and increase the visibility of your app's content.&lt;/p&gt;
&lt;p&gt;Resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/corespotlight"&gt;https://developer.apple.com/documentation/corespotlight&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Difference between map, flatMap, compactMap</title>    <link>https://wesleydegroot.nl/blog/map,flatmap,compactmap</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/map,flatmap,compactmap</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:55 +0200</pubDate>
    <category>Swift</category>
    <category>map</category>
    <category>flatmap</category>
    <category>compactMap</category>
    <description>&lt;p&gt;In this post, we'll explore the differences between &lt;code&gt;map&lt;/code&gt;, &lt;code&gt;flatMap&lt;/code&gt;, and &lt;code&gt;compactMap&lt;/code&gt; in Swift. These three methods are commonly used when working with collections, and understanding their distinctions is crucial for writing clean and efficient code.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;map&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;map&lt;/code&gt; is a higher-order function that transforms each element of a collection using a provided closure. It returns a new collection with the transformed elements, leaving the original collection unchanged. The closure passed to &lt;code&gt;map&lt;/code&gt; takes an element of the collection as input and returns a transformed value.&lt;/p&gt;
&lt;p&gt;Here's a simple example of using &lt;code&gt;map&lt;/code&gt; to double each element of an array of integers:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;let numbers = [1, 2, 3, 4, 5]

let doubledNumbers = numbers.map { $0 * 2 }

print(doubledNumbers) // Output: [2, 4, 6, 8, 10]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, the closure &lt;code&gt;{ $0 * 2 }&lt;/code&gt; is applied to each element of the &lt;code&gt;numbers&lt;/code&gt; array, resulting in a new array with each element doubled.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;map()&lt;/code&gt; can also transform each element of a collection using a provided closure and return a new collection with the transformed elements.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;let numbers = [1, 2, 3, 4, 5]
print(numbers) // Output: [1, 2, 3, 4, 5]

let strings = numbers.map { String($0) }
print(strings) // Output: ["1", "2", "3", "4", "5"]

let maybeNumbers = strings.map { Int($0) } // [1, 2, 3, 4, 5]
print(maybeNumbers) // Output: [Optional(1), Optional(2), Optional(3), Optional(4), Optional(5)]

let unwrappedNumbers = maybeNumbers.compactMap { $0 }
print(unwrappedNumbers) // Output: [1, 2, 3, 4, 5]&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;What is &lt;code&gt;flatMap&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;flatMap&lt;/code&gt; is a higher-order function that transforms each element of a collection using a provided closure and then flattens the resulting collection of collections into a single collection. It is particularly useful when working with nested collections or when you want to remove &lt;code&gt;nil&lt;/code&gt; values from a collection.&lt;/p&gt;
&lt;p&gt;Here's an example of using &lt;code&gt;flatMap&lt;/code&gt; to flatten an array of arrays:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;let nestedNumbers = [[1, 2], [3, 4], [5, 6]]

let flattenedNumbers = nestedNumbers.flatMap { $0 }

print(flattenedNumbers) // Output: [1, 2, 3, 4, 5, 6]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, the closure &lt;code&gt;{ $0 }&lt;/code&gt; is applied to each element of the &lt;code&gt;nestedNumbers&lt;/code&gt; array, resulting in a new array with the nested arrays flattened into a single array.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;compactMap&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;compactMap&lt;/code&gt; is a higher-order function that transforms each element of a collection using a provided closure and then removes any &lt;code&gt;nil&lt;/code&gt; values from the resulting collection. It is particularly useful when working with collections that may contain optional values.&lt;/p&gt;
&lt;p&gt;Here's an example of using &lt;code&gt;compactMap&lt;/code&gt; to remove &lt;code&gt;nil&lt;/code&gt; values from an array of optional integers:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;let optionalNumbers: [Int?] = [1, nil, 3, nil, 5]

let nonNilNumbers = optionalNumbers.compactMap { $0 }

print(nonNilNumbers) // Output: [1, 3, 5]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, the closure &lt;code&gt;{ $0 }&lt;/code&gt; is applied to each element of the &lt;code&gt;optionalNumbers&lt;/code&gt; array, resulting in a new array with the &lt;code&gt;nil&lt;/code&gt; values removed.&lt;/p&gt;
&lt;h2&gt;Use Cases&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Use &lt;code&gt;map&lt;/code&gt; when you want to transform each element of a collection.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use &lt;code&gt;flatMap&lt;/code&gt; when you want to transform each element of a collection and flatten the resulting collection of collections.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use &lt;code&gt;compactMap&lt;/code&gt; when you want to transform each element of a collection and remove any &lt;code&gt;nil&lt;/code&gt; values.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In summary, &lt;code&gt;map&lt;/code&gt;, &lt;code&gt;flatMap&lt;/code&gt;, and &lt;code&gt;compactMap&lt;/code&gt; are powerful tools for working with collections in Swift. By understanding their differences and use cases, you can write cleaner and more efficient code when transforming and filtering collections.&lt;/p&gt;
&lt;p&gt;Resources:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://docs.swift.org/swift-book/documentation/the-swift-programming-language/collectiontypes/#Iterating-Over-an-Array"&gt;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/collectiontypes/#Iterating-Over-an-Array&lt;/a&gt;&lt;/p&gt;</description>
  </item>
  <item>
    <title>Enums</title>    <link>https://wesleydegroot.nl/blog/enums</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/enums</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:55 +0200</pubDate>
    <category>Swift</category>
    <category>Enums</category>
    <description>&lt;p&gt;In this post, we'll explore enums in Swift, a powerful feature that allows you to define a group of related values. Enums are commonly used to represent a fixed set of options or states in your code, making it easier to work with and reason about different cases.&lt;/p&gt;
&lt;h2&gt;What are &lt;code&gt;enum&lt;/code&gt;s?&lt;/h2&gt;
&lt;p&gt;An &lt;code&gt;enum&lt;/code&gt; in Swift is a data type that defines a group of related values. Each value in an enum is called a case, and you can define as many cases as needed to represent the different options or states of a particular type.&lt;/p&gt;
&lt;p&gt;Here's an example of defining an enum in Swift:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;enum CompassPoint {
    case north
    case south
    case east
    case west
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, we define an enum &lt;code&gt;CompassPoint&lt;/code&gt; with four cases: &lt;code&gt;north&lt;/code&gt;, &lt;code&gt;south&lt;/code&gt;, &lt;code&gt;east&lt;/code&gt;, and &lt;code&gt;west&lt;/code&gt;. Each case represents a direction on a compass, and you can use these cases to work with compass directions in your code.&lt;/p&gt;
&lt;h2&gt;Using &lt;code&gt;enum&lt;/code&gt;s&lt;/h2&gt;
&lt;p&gt;Once you've defined an enum, you can use it to create instances of the enum type and work with the different cases. Here's an example of using the &lt;code&gt;CompassPoint&lt;/code&gt; enum:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;let direction: CompassPoint = .north

switch direction {
    case .north:
        print("Heading north")
    case .south:
        print("Heading south")
    case .east:
        print("Heading east")
    case .west:
        print("Heading west")
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, we create an instance of the &lt;code&gt;CompassPoint&lt;/code&gt; enum called &lt;code&gt;direction&lt;/code&gt; and set it to &lt;code&gt;.north&lt;/code&gt;. We then use a &lt;code&gt;switch&lt;/code&gt; statement to check the value of &lt;code&gt;direction&lt;/code&gt; and print a message based on the case.&lt;/p&gt;
&lt;p&gt;Enums are a powerful tool for representing a fixed set of options or states in your code. They help make your code more readable, maintainable, and robust by providing a clear and concise way to work with related values.&lt;/p&gt;
&lt;h2&gt;Associated Values&lt;/h2&gt;
&lt;p&gt;Enums in Swift can also have associated values, which allow you to attach additional data to each case. This is useful when you need to store extra information along with the case.&lt;/p&gt;
&lt;p&gt;Here's an example of an enum with associated values:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;enum PokemonState {
    case idle
    case walking(speed: Double)
    case running(speed: Double)
    case attacking(damage: Int)
    case catched(pokemon: String)
    case fainted
}

let dragonite: PokemonState = .attacking(damage: 10)
let trainer: PokemonState = .catched(pokemon: "Pikachu")
let lugia: PokemonState = .idle

switch dragonite {
    case .idle:
        print("Dragonite is idle")
    case .walking(let speed):
        print("Dragonite is walking at speed \(speed)")
    case .running(let speed):
        print("Dragonite is running at speed \(speed)")
    case .attacking(let damage):
        print("Dragonite is attacking with damage \(damage)")
    case .catched(let pokemon):
        print("Dragonite has catched \(pokemon)")
    case .fainted:
        print("Dragonite has fainted")
} // Output: Dragonite is attacking with damage 10

switch trainer {
    case .idle:
        print("Trainer is idle")
    case .walking(let speed):
        print("Trainer is walking at speed \(speed)")
    case .running(let speed):
        print("Trainer is running at speed \(speed)")
    case .attacking(let damage):
        print("Trainer is attacking with damage \(damage)")
    case .catched(let pokemon):
        print("Trainer has catched \(pokemon)")
    case .fainted:
        print("Trainer has fainted")
} // Output: Trainer has catched Pikachu

switch lugia {
    case .idle:
        print("Lugia is idle")
    case .walking(let speed):
        print("Lugia is walking at speed \(speed)")
    case .running(let speed):
        print("Lugia is running at speed \(speed)")
    case .attacking(let damage):
        print("Lugia is attacking with damage \(damage)")
    case .catched(let pokemon):
        print("Lugia has catched \(pokemon)")
    case .fainted:
        print("Lugia has fainted")
} // Output: Lugia is idle&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, we define an enum &lt;code&gt;PokemonState&lt;/code&gt; with cases representing different states of a Pokemon. Each case has an associated value that provides additional information about the state. We then create instances of the enum with different cases and use a &lt;code&gt;switch&lt;/code&gt; statement to handle each case.&lt;/p&gt;
&lt;p&gt;Enums with associated values are a powerful feature of Swift that allows you to model complex data structures and represent different states in your code.&lt;/p&gt;
&lt;h2&gt;Raw Values&lt;/h2&gt;
&lt;p&gt;Enums in Swift can also have raw values, which are pre-defined values associated with each case. Raw values are useful when you need to map an enum case to a specific value, such as an integer or string.&lt;/p&gt;
&lt;p&gt;Here's an example of an enum with raw values:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;enum Pokemon: Int {
    case pikachu = 25
    case dragonite = 149
    case lugia = 249
}

let pikachuId = Pokemon.pikachu.rawValue
let dragoniteId = Pokemon.dragonite.rawValue
let lugiaId = Pokemon.lugia.rawValue

print("Pikachu Pokedex number: #\(pikachuId)")
print("Dragonite Pokedex number: #\(dragoniteId)")
print("Lugia Pokedex number: #\(lugiaId)")&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, we define an enum &lt;code&gt;Pokemon&lt;/code&gt; with raw values of type &lt;code&gt;Int&lt;/code&gt;. Each case is associated with a specific integer value, and we access the raw value of each case using the &lt;code&gt;rawValue&lt;/code&gt; property.&lt;/p&gt;
&lt;p&gt;Enums with raw values are a convenient way to map enum cases to specific values and provide a consistent way to work with related values in your code.&lt;/p&gt;
&lt;h2&gt;Use Cases&lt;/h2&gt;
&lt;p&gt;Enums are commonly used in Swift to represent a fixed set of options or states in your code. Here are some common use cases for enums:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Representing different states of an object or system&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Defining a set of options for user input&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Modeling different types of data or entities&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Mapping enum cases to specific values or behaviors&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By using enums in your code, you can make it more readable, maintainable, and robust by providing a clear and concise way to work with related values.&lt;/p&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;p&gt;Enums in Swift are powerful tools for modeling data and representing different states in your code. However, there are some limitations and considerations to keep in mind when working with enums:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Enums with associated values can become complex and hard to manage if they have too many cases or associated data.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Raw values in enums are limited to specific types, such as integers, strings, and floating-point numbers.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Enums with raw values must have unique raw values for each case to avoid conflicts.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When using enums in your code, consider the complexity of the data you're modeling and choose the appropriate enum type (plain, associated values, or raw values) based on your needs.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Enums are a fundamental part of Swift that provide a flexible and expressive way to define a group of related values. By understanding how to define enums, work with associated values and raw values, and use enums in your code, you can write cleaner, more maintainable, and more robust code that is easier to reason about and work with.&lt;/p&gt;
&lt;p&gt;Resources:&lt;br&gt;
&lt;a href="https://docs.swift.org/swift-book/documentation/the-swift-programming-language/enumerations/"&gt;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/enumerations/&lt;/a&gt;&lt;/p&gt;</description>
  </item>
  <item>
    <title>What is @frozen</title>    <link>https://wesleydegroot.nl/blog/@frozen</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/@frozen</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:56 +0200</pubDate>
    <category>Swift</category>
    <category>Frozen</category>
    <description>&lt;p&gt;In this post, we'll explore the &lt;code&gt;@frozen&lt;/code&gt; attribute in Swift, a compiler directive that can be applied to &lt;code&gt;enum&lt;/code&gt; declarations to optimize memory layout and improve performance. By marking an &lt;code&gt;enum&lt;/code&gt; as &lt;code&gt;@frozen&lt;/code&gt;, you can enable certain optimizations that can make your code more efficient.&lt;/p&gt;
&lt;h2&gt;What does &lt;code&gt;@frozen&lt;/code&gt; do?&lt;/h2&gt;
&lt;p&gt;When you mark an &lt;code&gt;enum&lt;/code&gt; or &lt;code&gt;struct&lt;/code&gt; as &lt;code&gt;@frozen&lt;/code&gt;, you're telling the Swift compiler that the &lt;code&gt;enum&lt;/code&gt;'s memory layout is fixed and will not change (is simply a promise that the &lt;strong&gt;public interface of these types will never change&lt;/strong&gt;). This allows the compiler to make certain assumptions about the &lt;code&gt;enum&lt;/code&gt; that can lead to performance improvements.&lt;/p&gt;
&lt;h2&gt;Use Case for &lt;code&gt;@frozen&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;One common use case for &lt;code&gt;@frozen&lt;/code&gt; is when you have an &lt;code&gt;enum&lt;/code&gt; with a fixed set of cases that you know will never change. By marking the &lt;code&gt;enum&lt;/code&gt; as &lt;code&gt;@frozen&lt;/code&gt;, you enable the compiler to optimize the memory layout of the &lt;code&gt;enum&lt;/code&gt; and generate more efficient code.&lt;/p&gt;
&lt;p&gt;Here are two examples of using &lt;code&gt;@frozen&lt;/code&gt; with an &lt;code&gt;enum&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;@frozen
struct Point {
  var x: Double
  var y: Double
}

@frozen
enum Direction {
    case north
    case south
    case east
    case west
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, the &lt;code&gt;Poin&lt;/code&gt; and &lt;code&gt;Direction&lt;/code&gt; &lt;code&gt;enum&lt;/code&gt; is marked as &lt;code&gt;@frozen&lt;/code&gt; because we know that the set of cases will never change. This allows the compiler to optimize the memory layout of the &lt;code&gt;enum&lt;/code&gt; and generate more efficient code when working with &lt;code&gt;Direction&lt;/code&gt; instances.&lt;/p&gt;
&lt;h2&gt;Benefits of &lt;code&gt;@frozen&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;By using the &lt;code&gt;@frozen&lt;/code&gt; attribute, you can achieve the following benefits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Improved Performance&lt;/strong&gt;: The compiler can make certain assumptions about the &lt;code&gt;enum&lt;/code&gt; that can lead to performance improvements, such as better memory layout and more efficient code generation.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Optimized Memory Layout&lt;/strong&gt;: Marking an &lt;code&gt;enum&lt;/code&gt; as &lt;code&gt;@frozen&lt;/code&gt; allows the compiler to optimize the memory layout of the &lt;code&gt;enum&lt;/code&gt;, reducing the size of instances and improving memory access patterns.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Predictable Behavior&lt;/strong&gt;: By explicitly marking an &lt;code&gt;enum&lt;/code&gt; as &lt;code&gt;@frozen&lt;/code&gt;, you make it clear to other developers that the set of cases will not change, leading to more predictable behavior.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Example of Use Case&lt;/h2&gt;
&lt;p&gt;Here's an example of using &lt;code&gt;@frozen&lt;/code&gt; with an &lt;code&gt;enum&lt;/code&gt; that represents different types of shapes:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;@frozen
enum Shape {
    case circle(radius: Double)
    case square(side: Double)
    case triangle(base: Double, height: Double)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, the &lt;code&gt;Shape&lt;/code&gt; &lt;code&gt;enum&lt;/code&gt; is marked as &lt;code&gt;@frozen&lt;/code&gt; because the set of cases is fixed and will not change. This allows the compiler to optimize the memory layout of &lt;code&gt;Shape&lt;/code&gt; instances and generate more efficient code when working with shapes.&lt;/p&gt;
&lt;h2&gt;ABI Stability&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;@frozen&lt;/code&gt; attribute is also important for maintaining ABI stability in Swift. By marking an &lt;code&gt;enum&lt;/code&gt; as &lt;code&gt;@frozen&lt;/code&gt;, you ensure that the memory layout of the &lt;code&gt;enum&lt;/code&gt; is fixed and will not change in future versions of the library or framework. This is crucial for ensuring compatibility between different versions of Swift.&lt;/p&gt;
&lt;h2&gt;What is an Application Binary Interface?&lt;/h2&gt;
&lt;p&gt;An Application Binary Interface (ABI) defines how functions, data structures, and other elements are represented in binary form and interact with each other at runtime. ABI stability is important for ensuring that code compiled with different versions of a library or framework can work together without issues.&lt;/p&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;p&gt;While &lt;code&gt;@frozen&lt;/code&gt; can provide performance benefits, there are a few caveats to keep in mind:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Immutable Set of Cases&lt;/strong&gt;: Once you mark an &lt;code&gt;enum&lt;/code&gt; as &lt;code&gt;@frozen&lt;/code&gt;, you cannot add or remove cases from it. This can limit the flexibility of the &lt;code&gt;enum&lt;/code&gt; if you need to make changes in the future.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Compatibility&lt;/strong&gt;: The &lt;code&gt;@frozen&lt;/code&gt; attribute is only available in Swift 5.1 and later, so if you need to maintain compatibility with older versions of Swift, you may not be able to use it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Limited Use Cases&lt;/strong&gt;: &lt;code&gt;@frozen&lt;/code&gt; is most beneficial for &lt;code&gt;enum&lt;/code&gt;s with a fixed set of cases that will never change. If your &lt;code&gt;enum&lt;/code&gt; is likely to evolve over time, using &lt;code&gt;@frozen&lt;/code&gt; may not be appropriate.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;In this post, we've explored the &lt;code&gt;@frozen&lt;/code&gt; attribute in Swift and how it can be used to optimize the memory layout and improve the performance of &lt;code&gt;enum&lt;/code&gt; declarations. By marking an &lt;code&gt;enum&lt;/code&gt; as &lt;code&gt;@frozen&lt;/code&gt;, you can enable certain optimizations that can make your code more efficient and predictable.&lt;/p&gt;
&lt;p&gt;Resources:&lt;/p&gt;</description>
  </item>
  <item>
    <title>iOS Settings URL's</title>    <link>https://wesleydegroot.nl/blog/ios-settings-urls</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/ios-settings-urls</guid>
    <pubDate>Fri, 21 Nov 2025 16:15:08 +0100</pubDate>
    <category>Swift</category>
    <category>iOS</category>
    <category>Settings</category>
    <description>&lt;p&gt;This is a list of internal URLs for settings on your iPhone, iPad, ...&lt;br&gt;
Please note that this list will be updated if required, if you see something which is not right, please drop me a message and i'll try to fix it.&lt;br&gt;
I included a guide &lt;a href='#getTheLatestValues'&gt;how to extract the newest data&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Updated for iOS 26.1 (21-NOV-2025)&lt;/h2&gt;
&lt;h3&gt;Exposure Notifications&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=EXPOSURE_NOTIFICATION'&gt;prefs:root=EXPOSURE_NOTIFICATION&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Picture in Picture&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=PiP_SPEC'&gt;prefs:root=General&amp;amp;path=PiP_SPEC&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Trackpad &amp;amp; Mouse&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=POINTERS'&gt;prefs:root=General&amp;amp;path=POINTERS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Trackpad&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=POINTERS'&gt;prefs:root=General&amp;amp;path=POINTERS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;NFC&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=NFC_LINK'&gt;prefs:root=General&amp;amp;path=NFC_LINK&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;VPN&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=VPN'&gt;prefs:root=General&amp;amp;path=VPN&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;CarPlay&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=CARPLAY'&gt;prefs:root=General&amp;amp;path=CARPLAY&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;General&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General'&gt;prefs:root=General&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;About&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=About'&gt;prefs:root=General&amp;amp;path=About&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Legal &amp;amp; Regulatory&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=LEGAL_AND_REGULATORY'&gt;prefs:root=General&amp;amp;path=LEGAL_AND_REGULATORY&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Warranty&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=LEGAL_AND_REGULATORY#Warranty'&gt;prefs:root=General&amp;amp;path=LEGAL_AND_REGULATORY#Warranty&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Trusted Certificates&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=About/CERT_TRUST_SETTINGS'&gt;prefs:root=General&amp;amp;path=About/CERT_TRUST_SETTINGS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Software Update&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=SOFTWARE_UPDATE_LINK'&gt;prefs:root=General&amp;amp;path=SOFTWARE_UPDATE_LINK&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;AirDrop&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=AIRDROP_LINK'&gt;prefs:root=General&amp;amp;path=AIRDROP_LINK&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Bringing Devices Together&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=AIRDROP_LINK#AIRDROP_NFC_ID'&gt;prefs:root=General&amp;amp;path=AIRDROP_LINK#AIRDROP_NFC_ID&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Home Button&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=HOME_BUTTON'&gt;prefs:root=General&amp;amp;path=HOME_BUTTON&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Side Switch&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General#Rotation_Switch_Action_Group'&gt;prefs:root=General#Rotation_Switch_Action_Group&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Background App Refresh&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=AUTO_CONTENT_DOWNLOAD'&gt;prefs:root=General&amp;amp;path=AUTO_CONTENT_DOWNLOAD&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Date &amp;amp; Time&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=DATE_AND_TIME'&gt;prefs:root=General&amp;amp;path=DATE_AND_TIME&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;TV Out&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=TV_OUT'&gt;prefs:root=General&amp;amp;path=TV_OUT&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Reset&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=Reset'&gt;prefs:root=General&amp;amp;path=Reset&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Reset All Settings&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=Reset#settingsErase'&gt;prefs:root=General&amp;amp;path=Reset#settingsErase&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Erase All Content and Settings&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=Reset#fullErase'&gt;prefs:root=General&amp;amp;path=Reset#fullErase&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Reset Network Settings&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=Reset#RESET_NETWORK_LABEL'&gt;prefs:root=General&amp;amp;path=Reset#RESET_NETWORK_LABEL&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Remove All Cellular Data Plans&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=Reset#cellularErase'&gt;prefs:root=General&amp;amp;path=Reset#cellularErase&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Subscriber Services&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=Reset#SUBSCRIBER_SERVICES_ID'&gt;prefs:root=General&amp;amp;path=Reset#SUBSCRIBER_SERVICES_ID&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Reset Keyboard Dictionary&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=Reset#RESET_KEYBOARD_DICTIONARY_LABEL'&gt;prefs:root=General&amp;amp;path=Reset#RESET_KEYBOARD_DICTIONARY_LABEL&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Reset Home Screen Layout&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=Reset#RESET_ICONS_LABEL'&gt;prefs:root=General&amp;amp;path=Reset#RESET_ICONS_LABEL&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Reset Location &amp;amp; Privacy&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=Reset#RESET_PRIVACY_LABEL'&gt;prefs:root=General&amp;amp;path=Reset#RESET_PRIVACY_LABEL&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Shut Down&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General#SHUTDOWN_LABEL'&gt;prefs:root=General#SHUTDOWN_LABEL&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Handoff&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=CONTINUITY_SPEC#CONTINUITY'&gt;prefs:root=General&amp;amp;path=CONTINUITY_SPEC#CONTINUITY&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Transfer to HomePod&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=CONTINUITY_SPEC#TRANSFER_TO_HOMEPOD'&gt;prefs:root=General&amp;amp;path=CONTINUITY_SPEC#TRANSFER_TO_HOMEPOD&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Automatically AirPlay&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=CONTINUITY_SPEC#AUTOMATICALLY_AIRPLAY'&gt;prefs:root=General&amp;amp;path=CONTINUITY_SPEC#AUTOMATICALLY_AIRPLAY&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;AirPlay &amp;amp; Continuity&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=CONTINUITY_SPEC'&gt;prefs:root=General&amp;amp;path=CONTINUITY_SPEC&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Continuity Camera&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=CONTINUITY_SPEC#WOMBAT_CAMERA'&gt;prefs:root=General&amp;amp;path=CONTINUITY_SPEC#WOMBAT_CAMERA&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Serial Number&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=About#SerialNumber'&gt;prefs:root=General&amp;amp;path=About#SerialNumber&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Model Number&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=About#ProductModel'&gt;prefs:root=General&amp;amp;path=About#ProductModel&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Model Name&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=About#ProductModelName'&gt;prefs:root=General&amp;amp;path=About#ProductModelName&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;OS_VERSION&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=About/SW_VERSION_SPECIFIER'&gt;prefs:root=General&amp;amp;path=About/SW_VERSION_SPECIFIER&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Touch ID &amp;amp; Passcode&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=PASSCODE'&gt;prefs:root=PASSCODE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Face ID &amp;amp; Passcode&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=PASSCODE'&gt;prefs:root=PASSCODE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Passcode&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=PASSCODE'&gt;prefs:root=PASSCODE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Turn Passcode Off&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=PASSCODE#PASSCODE_OFF'&gt;prefs:root=PASSCODE#PASSCODE_OFF&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Change Passcode&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=PASSCODE#CHANGE_PASSCODE'&gt;prefs:root=PASSCODE#CHANGE_PASSCODE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Require Passcode&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=PASSCODE&amp;amp;path=PASSCODE_REQ'&gt;prefs:root=PASSCODE&amp;amp;path=PASSCODE_REQ&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Voice Dial&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=PASSCODE#VOICE_DIAL'&gt;prefs:root=PASSCODE#VOICE_DIAL&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Allow Access When Locked&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=PASSCODE#ALLOW_ACCESS_WHEN_LOCKED'&gt;prefs:root=PASSCODE#ALLOW_ACCESS_WHEN_LOCKED&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Reply with Message&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=PASSCODE#REPLY_WITH_MESSAGE_SWITCH'&gt;prefs:root=PASSCODE#REPLY_WITH_MESSAGE_SWITCH&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Home Control&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=PASSCODE#HOME_CONTROL_SWITCH'&gt;prefs:root=PASSCODE#HOME_CONTROL_SWITCH&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Wallet&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=PASSCODE#WALLET_SWITCH'&gt;prefs:root=PASSCODE#WALLET_SWITCH&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Return Missed Calls&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=PASSCODE#RETURN_MISSED_CALLS_SWITCH'&gt;prefs:root=PASSCODE#RETURN_MISSED_CALLS_SWITCH&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Erase Data&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=PASSCODE#WIPE_DEVICE'&gt;prefs:root=PASSCODE#WIPE_DEVICE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Unlock with Apple Watch&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=PASSCODE#AUTO_UNLOCK_DEVICES_GROUP'&gt;prefs:root=PASSCODE#AUTO_UNLOCK_DEVICES_GROUP&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Stolen Device Protection&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=PASSCODE#DTO_GROUP_ID'&gt;prefs:root=PASSCODE#DTO_GROUP_ID&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Apple Pencil&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Pencil'&gt;prefs:root=Pencil&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Apple Pencil&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Pencil#PrefersPencilDraws'&gt;prefs:root=Pencil#PrefersPencilDraws&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Apple Pencil&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Pencil#PencilTextInput'&gt;prefs:root=Pencil#PencilTextInput&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Podcasts&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=PODCASTS'&gt;prefs:root=PODCASTS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Share My Location&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;amp;path=LOCATION/LOCATION_SHARING'&gt;prefs:root=Privacy&amp;amp;path=LOCATION/LOCATION_SHARING&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Privacy&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy'&gt;prefs:root=Privacy&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Location&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;amp;path=LOCATION'&gt;prefs:root=Privacy&amp;amp;path=LOCATION&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Contacts&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;amp;path=CONTACTS'&gt;prefs:root=Privacy&amp;amp;path=CONTACTS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Calendars&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;amp;path=CALENDARS'&gt;prefs:root=Privacy&amp;amp;path=CALENDARS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Reminders&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;amp;path=REMINDERS'&gt;prefs:root=Privacy&amp;amp;path=REMINDERS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Photos&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;amp;path=PHOTOS'&gt;prefs:root=Privacy&amp;amp;path=PHOTOS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Bluetooth Sharing&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;amp;path=BT_PERIPHERAL'&gt;prefs:root=Privacy&amp;amp;path=BT_PERIPHERAL&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Microphone&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;amp;path=MICROPHONE'&gt;prefs:root=Privacy&amp;amp;path=MICROPHONE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Speech Recognition&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;amp;path=SPEECH_RECOGNITION'&gt;prefs:root=Privacy&amp;amp;path=SPEECH_RECOGNITION&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Camera&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;amp;path=CAMERA'&gt;prefs:root=Privacy&amp;amp;path=CAMERA&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Home&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;amp;path=WILLOW'&gt;prefs:root=Privacy&amp;amp;path=WILLOW&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Media &amp;amp; Apple Music&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;amp;path=MEDIALIBRARY'&gt;prefs:root=Privacy&amp;amp;path=MEDIALIBRARY&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Analytics&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;amp;path=PROBLEM_REPORTING'&gt;prefs:root=Privacy&amp;amp;path=PROBLEM_REPORTING&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Advertising&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;amp;path=ADVERTISING'&gt;prefs:root=Privacy&amp;amp;path=ADVERTISING&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Files and Folders&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;amp;path=FILEACCESS'&gt;prefs:root=Privacy&amp;amp;path=FILEACCESS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Tracking&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;amp;path=USER_TRACKING'&gt;prefs:root=Privacy&amp;amp;path=USER_TRACKING&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;App Privacy Report&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;amp;path=PRIVACY_REPORT'&gt;prefs:root=Privacy&amp;amp;path=PRIVACY_REPORT&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Lockdown Mode&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy#LOCKDOWN_MODE'&gt;prefs:root=Privacy#LOCKDOWN_MODE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Sensitive Content&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy#NUDITY_DETECTION'&gt;prefs:root=Privacy#NUDITY_DETECTION&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Emergency SOS&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=EMERGENCY_SOS'&gt;prefs:root=EMERGENCY_SOS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Call with Side Button&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=EMERGENCY_SOS#CALL_WITH_SIDE_BUTTON'&gt;prefs:root=EMERGENCY_SOS#CALL_WITH_SIDE_BUTTON&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Auto Call&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=EMERGENCY_SOS#AUTO_CALL'&gt;prefs:root=EMERGENCY_SOS#AUTO_CALL&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Emergency Contacts&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=EMERGENCY_SOS#EMERGENCY_CONTACTS'&gt;prefs:root=EMERGENCY_SOS#EMERGENCY_CONTACTS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Countdown Sound&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=EMERGENCY_SOS#ALARM_SOUND_SWITCH'&gt;prefs:root=EMERGENCY_SOS#ALARM_SOUND_SWITCH&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Lightning Adapters&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Sounds&amp;amp;path=HEADPHONE_LEVEL_LIMIT_SETTING/HEADPHONE_LIGHTNING_ADAPTERS'&gt;prefs:root=Sounds&amp;amp;path=HEADPHONE_LEVEL_LIMIT_SETTING/HEADPHONE_LIGHTNING_ADAPTERS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Headphone Notifications&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Sounds&amp;amp;path=HEADPHONE_LEVEL_LIMIT_SETTING#SHSHeadphoneWeeklyNotificationsKey'&gt;prefs:root=Sounds&amp;amp;path=HEADPHONE_LEVEL_LIMIT_SETTING#SHSHeadphoneWeeklyNotificationsKey&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Headphone Safety&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Sounds&amp;amp;path=HEADPHONE_LEVEL_LIMIT_SETTING'&gt;prefs:root=Sounds&amp;amp;path=HEADPHONE_LEVEL_LIMIT_SETTING&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Reduce Loud Audio&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Sounds&amp;amp;path=HEADPHONE_LEVEL_LIMIT_SETTING#SHSHeadphoneLevelLimitSwitchKey'&gt;prefs:root=Sounds&amp;amp;path=HEADPHONE_LEVEL_LIMIT_SETTING#SHSHeadphoneLevelLimitSwitchKey&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Sounds &amp;amp; Haptics&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Sounds'&gt;prefs:root=Sounds&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Sounds&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Sounds'&gt;prefs:root=Sounds&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Sound Effects&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Sounds&amp;amp;path=SOUND_EFFECTS'&gt;prefs:root=Sounds&amp;amp;path=SOUND_EFFECTS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Ringer and Alerts&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Sounds#RINGER_AND_ALERT_GROUP'&gt;prefs:root=Sounds#RINGER_AND_ALERT_GROUP&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Change with Buttons&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Sounds#CHANGE_WITH_BUTTONS'&gt;prefs:root=Sounds#CHANGE_WITH_BUTTONS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Sounds and Vibration Patterns&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Sounds#SOUNDS_ALERT_GROUP'&gt;prefs:root=Sounds#SOUNDS_ALERT_GROUP&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Ringtone&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Sounds&amp;amp;path=Ringtone'&gt;prefs:root=Sounds&amp;amp;path=Ringtone&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Text Tone&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Sounds&amp;amp;path=Text_Messages'&gt;prefs:root=Sounds&amp;amp;path=Text_Messages&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;New Voicemail&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Sounds&amp;amp;path=Voicemail'&gt;prefs:root=Sounds&amp;amp;path=Voicemail&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;New Mail&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Sounds&amp;amp;path=NEW_MAIL'&gt;prefs:root=Sounds&amp;amp;path=NEW_MAIL&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Sent Mail&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Sounds&amp;amp;path=SENT_MAIL'&gt;prefs:root=Sounds&amp;amp;path=SENT_MAIL&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Calendar Alerts&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Sounds&amp;amp;path=Calendar%20Alarm'&gt;prefs:root=Sounds&amp;amp;path=Calendar%20Alarm&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Reminder Alerts&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Sounds&amp;amp;path=Reminder%20Alerts'&gt;prefs:root=Sounds&amp;amp;path=Reminder%20Alerts&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Keyboard Clicks&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Sounds#KEYBOARD_SOUND_SWITCH'&gt;prefs:root=Sounds#KEYBOARD_SOUND_SWITCH&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Lock Sound&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Sounds#LOCK_SOUND_SWITCH'&gt;prefs:root=Sounds#LOCK_SOUND_SWITCH&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Personalized Spatial Audio&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Sounds&amp;amp;path=Reminder&amp;amp;path=Personalized%20Spatial%20Audio'&gt;prefs:root=Sounds&amp;amp;path=Reminder&amp;amp;path=Personalized%20Spatial%20Audio&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Compass&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=COMPASS'&gt;prefs:root=COMPASS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Use True North&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=COMPASS#USE_TRUE_NORTH'&gt;prefs:root=COMPASS#USE_TRUE_NORTH&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Control Center&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ControlCenter'&gt;prefs:root=ControlCenter&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Access Within Apps&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ControlCenter#ALLOWED_WITHIN_APPS'&gt;prefs:root=ControlCenter#ALLOWED_WITHIN_APPS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Customize Controls&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ControlCenter&amp;amp;path=CUSTOMIZE_CONTROLS'&gt;prefs:root=ControlCenter&amp;amp;path=CUSTOMIZE_CONTROLS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Multipath Networking&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS&amp;amp;path=MULTI_PATH_AGG'&gt;prefs:root=DEVELOPER_SETTINGS&amp;amp;path=MULTI_PATH_AGG&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Developer&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS'&gt;prefs:root=DEVELOPER_SETTINGS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Appearance&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS#UIAppearanceGroup'&gt;prefs:root=DEVELOPER_SETTINGS#UIAppearanceGroup&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Dark Appearance&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS#UIAppearanceSettings'&gt;prefs:root=DEVELOPER_SETTINGS#UIAppearanceSettings&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Paired Devices&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS#DTPairedDevicesGroup'&gt;prefs:root=DEVELOPER_SETTINGS#DTPairedDevicesGroup&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Clear Trusted Computers&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS#CLEAR_TRUSTED_COMPUTERS'&gt;prefs:root=DEVELOPER_SETTINGS#CLEAR_TRUSTED_COMPUTERS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Logging&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS&amp;amp;path=DTInstrumentsSettings'&gt;prefs:root=DEVELOPER_SETTINGS&amp;amp;path=DTInstrumentsSettings&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;UI Automation&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS#UIAGroup'&gt;prefs:root=DEVELOPER_SETTINGS#UIAGroup&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Enable UI Automation&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS#UIAGroup'&gt;prefs:root=DEVELOPER_SETTINGS#UIAGroup&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Networking&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS#NLCGroup'&gt;prefs:root=DEVELOPER_SETTINGS#NLCGroup&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Network Link Conditioner&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS&amp;amp;path=NLC'&gt;prefs:root=DEVELOPER_SETTINGS&amp;amp;path=NLC&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Multipath Networking&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS&amp;amp;path=MULTI_PATH_AGG#Multipath%20Networking'&gt;prefs:root=DEVELOPER_SETTINGS&amp;amp;path=MULTI_PATH_AGG#Multipath%20Networking&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Additional Logging&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS#ADDITIONAL_LOGGING'&gt;prefs:root=DEVELOPER_SETTINGS#ADDITIONAL_LOGGING&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Allow HTTP Services&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS#ALLOW_HTTP_SERVICES'&gt;prefs:root=DEVELOPER_SETTINGS#ALLOW_HTTP_SERVICES&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Disable Rate Limiting&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS#DISABLE_RATE_LIMITING'&gt;prefs:root=DEVELOPER_SETTINGS#DISABLE_RATE_LIMITING&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;NFC Pass Key Optional&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS#NFC_PASS_KEY_OPTIONAL'&gt;prefs:root=DEVELOPER_SETTINGS#NFC_PASS_KEY_OPTIONAL&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Media Services Testing&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS#MEDIA_SERVICES_TESTING'&gt;prefs:root=DEVELOPER_SETTINGS#MEDIA_SERVICES_TESTING&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Reset Media Services&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS#RESET_MEDIA_SERVICES'&gt;prefs:root=DEVELOPER_SETTINGS#RESET_MEDIA_SERVICES&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;News Testing&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS#NEWS_TESTING'&gt;prefs:root=DEVELOPER_SETTINGS#NEWS_TESTING&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Reset Local Data on Next Launch&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS#RESET_LOCAL_DATA_ON_NEXT_LAUNCH'&gt;prefs:root=DEVELOPER_SETTINGS#RESET_LOCAL_DATA_ON_NEXT_LAUNCH&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Media Player Framework Testing&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS#RoutineSettingsGroup'&gt;prefs:root=DEVELOPER_SETTINGS#RoutineSettingsGroup&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Playable Content API&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS&amp;amp;path=RoutineSettings'&gt;prefs:root=DEVELOPER_SETTINGS&amp;amp;path=RoutineSettings&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;TV Provider&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS&amp;amp;path=VideoSubscriberAccountSettings'&gt;prefs:root=DEVELOPER_SETTINGS&amp;amp;path=VideoSubscriberAccountSettings&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;ClassKit API&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS&amp;amp;path=ClassKitSettings'&gt;prefs:root=DEVELOPER_SETTINGS&amp;amp;path=ClassKitSettings&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;CoreSpotlight Testing&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS#CORESPOTLIGHT_TESTING'&gt;prefs:root=DEVELOPER_SETTINGS#CORESPOTLIGHT_TESTING&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Reindex All Items&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS#REINDEX_ALL_ITEMS'&gt;prefs:root=DEVELOPER_SETTINGS#REINDEX_ALL_ITEMS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Reindex All Items with Identifiers&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS#REINDEX_ALL_ITEMS_WITH_IDENTIFIERS'&gt;prefs:root=DEVELOPER_SETTINGS#REINDEX_ALL_ITEMS_WITH_IDENTIFIERS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Shortcuts Testing&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS#SIRI_ACTIONS_TESTING'&gt;prefs:root=DEVELOPER_SETTINGS#SIRI_ACTIONS_TESTING&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Display Recent Shortcuts&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS#DISPLAY_DONATIONS_SPOTLIGHT'&gt;prefs:root=DEVELOPER_SETTINGS#DISPLAY_DONATIONS_SPOTLIGHT&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Display Upcoming Media&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS#DISPLAY_UPCOMING_MEDIA'&gt;prefs:root=DEVELOPER_SETTINGS#DISPLAY_UPCOMING_MEDIA&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Display Donations on Lock Screen&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS#DISPLAY_DONATIONS_LOCKSCREEN'&gt;prefs:root=DEVELOPER_SETTINGS#DISPLAY_DONATIONS_LOCKSCREEN&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Force Sync Shortcuts to Watch&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS#SIRI_ACTIONS_SYNC_WATCHOS'&gt;prefs:root=DEVELOPER_SETTINGS#SIRI_ACTIONS_SYNC_WATCHOS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;MIDI-CI Testing&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS#MIDI_CI_API_BETA'&gt;prefs:root=DEVELOPER_SETTINGS#MIDI_CI_API_BETA&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Enable MIDI-CI&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS#MIDI_CI_API_BETA_ENABLE'&gt;prefs:root=DEVELOPER_SETTINGS#MIDI_CI_API_BETA_ENABLE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Hang Detection&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS#HANGTRACER_EXTERNAL_CONFIGURE'&gt;prefs:root=DEVELOPER_SETTINGS#HANGTRACER_EXTERNAL_CONFIGURE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Dictionary&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=DICTIONARY'&gt;prefs:root=General&amp;amp;path=DICTIONARY&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Health&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;amp;path=HEALTH'&gt;prefs:root=Privacy&amp;amp;path=HEALTH&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Health Data&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;amp;path=HEALTH_DATA'&gt;prefs:root=Privacy&amp;amp;path=HEALTH_DATA&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Health&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=HEALTH'&gt;prefs:root=HEALTH&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Health Data&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=HEALTH'&gt;prefs:root=HEALTH&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Home&lt;/h3&gt;
&lt;p&gt;&lt;a href='settings-navigation://com.apple.Settings.HomeKit'&gt;settings-navigation://com.apple.Settings.HomeKit&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Language &amp;amp; Region&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=INTERNATIONAL'&gt;prefs:root=General&amp;amp;path=INTERNATIONAL&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Other Languages…&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=INTERNATIONAL#NEW_PREFERRED_LANGUAGE'&gt;prefs:root=General&amp;amp;path=INTERNATIONAL#NEW_PREFERRED_LANGUAGE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Preferred Language Order&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=INTERNATIONAL#PREFERRED_LANGUAGE_GROUP'&gt;prefs:root=General&amp;amp;path=INTERNATIONAL#PREFERRED_LANGUAGE_GROUP&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Add Language…&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=INTERNATIONAL#ADD_PREFERRED_LANGUAGE'&gt;prefs:root=General&amp;amp;path=INTERNATIONAL#ADD_PREFERRED_LANGUAGE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Region&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=INTERNATIONAL/LOCALE'&gt;prefs:root=General&amp;amp;path=INTERNATIONAL/LOCALE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Numbers&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=INTERNATIONAL/NUMBERING_SYSTEM'&gt;prefs:root=General&amp;amp;path=INTERNATIONAL/NUMBERING_SYSTEM&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Calendar&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=INTERNATIONAL/CALENDAR'&gt;prefs:root=General&amp;amp;path=INTERNATIONAL/CALENDAR&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Temperature Unit&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=INTERNATIONAL/TEMPERATURE_UNIT'&gt;prefs:root=General&amp;amp;path=INTERNATIONAL/TEMPERATURE_UNIT&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Measure&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MEASURE'&gt;prefs:root=MEASURE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Measure Units&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MEASURE#MEASURE_UNITS'&gt;prefs:root=MEASURE#MEASURE_UNITS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Imperial&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MEASURE#Imperial'&gt;prefs:root=MEASURE#Imperial&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Metric&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MEASURE#Metric'&gt;prefs:root=MEASURE#Metric&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Music&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MUSIC'&gt;prefs:root=MUSIC&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Show Apple Music&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MUSIC#com.apple.Music%3AAppleMusicEnabled'&gt;prefs:root=MUSIC#com.apple.Music%3AAppleMusicEnabled&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Add Playlist Songs&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MUSIC#com.apple.Music%3AAddPlaylistSongsToMyMusicSwitch'&gt;prefs:root=MUSIC#com.apple.Music%3AAddPlaylistSongsToMyMusicSwitch&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Show Star Ratings&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MUSIC#com.apple.Music%3AShowStarRatings'&gt;prefs:root=MUSIC#com.apple.Music%3AShowStarRatings&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Sync Library&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MUSIC#com.apple.Music%3ACloudMusicLibraryEnabled'&gt;prefs:root=MUSIC#com.apple.Music%3ACloudMusicLibraryEnabled&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Cellular Data&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MUSIC&amp;amp;path=com.apple.Music%3ACellularData'&gt;prefs:root=MUSIC&amp;amp;path=com.apple.Music%3ACellularData&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Downloaded Music&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MUSIC&amp;amp;path=com.apple.Music%3AMusicUsageLink'&gt;prefs:root=MUSIC&amp;amp;path=com.apple.Music%3AMusicUsageLink&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Optimize Storage&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MUSIC&amp;amp;path=com.apple.Music%3AOptimizeStorage'&gt;prefs:root=MUSIC&amp;amp;path=com.apple.Music%3AOptimizeStorage&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Automatic Downloads&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MUSIC#com.apple.Music%3AMusicAutomaticDownload'&gt;prefs:root=MUSIC#com.apple.Music%3AMusicAutomaticDownload&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;EQ&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MUSIC&amp;amp;path=com.apple.Music%3AEQ'&gt;prefs:root=MUSIC&amp;amp;path=com.apple.Music%3AEQ&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Sound Check&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MUSIC#com.apple.Music%3ASoundCheck'&gt;prefs:root=MUSIC#com.apple.Music%3ASoundCheck&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Use Listening History&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MUSIC#com.apple.Music%3APrivateListening'&gt;prefs:root=MUSIC#com.apple.Music%3APrivateListening&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Notes&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=NOTES'&gt;prefs:root=NOTES&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Default Account&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=NOTES&amp;amp;path=Default%20Account'&gt;prefs:root=NOTES&amp;amp;path=Default%20Account&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Password&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=NOTES&amp;amp;path=Password'&gt;prefs:root=NOTES&amp;amp;path=Password&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Sort Notes By&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=NOTES&amp;amp;path=Sort%20Notes%20By'&gt;prefs:root=NOTES&amp;amp;path=Sort%20Notes%20By&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;New Notes Start With&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=NOTES&amp;amp;path=New%20Notes%20Start%20With'&gt;prefs:root=NOTES&amp;amp;path=New%20Notes%20Start%20With&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Sort Checked Items&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=NOTES&amp;amp;path=Sort%20Checked%20Items'&gt;prefs:root=NOTES&amp;amp;path=Sort%20Checked%20Items&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Lines &amp;amp; Grids&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=NOTES&amp;amp;path=Lines%20%26%20Grids'&gt;prefs:root=NOTES&amp;amp;path=Lines%20%26%20Grids&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Save to Photos&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=NOTES#Save%20to%20Photos'&gt;prefs:root=NOTES#Save%20to%20Photos&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Access Notes from Lock Screen&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=NOTES&amp;amp;path=Access%20Notes%20from%20Lock%20Screen'&gt;prefs:root=NOTES&amp;amp;path=Access%20Notes%20from%20Lock%20Screen&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Notifications&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=NOTIFICATIONS_ID'&gt;prefs:root=NOTIFICATIONS_ID&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Messages&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MESSAGES'&gt;prefs:root=MESSAGES&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Screen Time&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SCREEN_TIME'&gt;prefs:root=SCREEN_TIME&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Automatic Updates&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=SOFTWARE_UPDATE_LINK/SUAutomaticUpdateButton'&gt;prefs:root=General&amp;amp;path=SOFTWARE_UPDATE_LINK/SUAutomaticUpdateButton&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Translate&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=TRANSLATE'&gt;prefs:root=TRANSLATE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;On-Device Mode&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=TRANSLATE#OnDeviceOnly'&gt;prefs:root=TRANSLATE#OnDeviceOnly&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;TV Provider&lt;/h3&gt;
&lt;p&gt;&lt;a href='settings-navigation://com.apple.Settings.TVProvider'&gt;settings-navigation://com.apple.Settings.TVProvider&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Voice Memos&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=VOICE_MEMOS'&gt;prefs:root=VOICE_MEMOS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Clear Deleted&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=VOICE_MEMOS&amp;amp;path=RCVoiceMemosRecentlyDeletedWindowKey'&gt;prefs:root=VOICE_MEMOS&amp;amp;path=RCVoiceMemosRecentlyDeletedWindowKey&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Audio Quality&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=VOICE_MEMOS&amp;amp;path=RCVoiceMemosAudioQualityKey'&gt;prefs:root=VOICE_MEMOS&amp;amp;path=RCVoiceMemosAudioQualityKey&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Location-based Naming&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=VOICE_MEMOS#RCVoiceMemosUseLocationBasedNaming'&gt;prefs:root=VOICE_MEMOS#RCVoiceMemosUseLocationBasedNaming&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Apple ProRaw&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CAMERA&amp;amp;path=CameraFormatsSettingsList#CAMUserPreferenceEnableLinearDNGControl'&gt;prefs:root=CAMERA&amp;amp;path=CameraFormatsSettingsList#CAMUserPreferenceEnableLinearDNGControl&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Apple ProRaw&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CAMERA&amp;amp;path=CameraFormatsSettingsList#CAMUserPreferenceEnableLinearDNGControl'&gt;prefs:root=CAMERA&amp;amp;path=CameraFormatsSettingsList#CAMUserPreferenceEnableLinearDNGControl&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;HDR Video&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CAMERA&amp;amp;path=Record%20Video#HDR%20Video'&gt;prefs:root=CAMERA&amp;amp;path=Record%20Video#HDR%20Video&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Camera&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CAMERA'&gt;prefs:root=CAMERA&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Preserve Settings&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CAMERA&amp;amp;path=CameraPreserveSettingsSwitch'&gt;prefs:root=CAMERA&amp;amp;path=CameraPreserveSettingsSwitch&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Camera Mode&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CAMERA&amp;amp;path=CameraPreserveSettingsSwitch#CAMUserPreferencePreserveCaptureMode'&gt;prefs:root=CAMERA&amp;amp;path=CameraPreserveSettingsSwitch#CAMUserPreferencePreserveCaptureMode&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Grid&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CAMERA#CameraGridSwitch'&gt;prefs:root=CAMERA#CameraGridSwitch&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Scan QR Codes&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CAMERA#CameraQRBannerSwitch'&gt;prefs:root=CAMERA#CameraQRBannerSwitch&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Formats&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CAMERA&amp;amp;path=CameraFormatsSettingsList'&gt;prefs:root=CAMERA&amp;amp;path=CameraFormatsSettingsList&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;FaceTime&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=FACETIME'&gt;prefs:root=FACETIME&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Contacts&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CONTACTS'&gt;prefs:root=CONTACTS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Allow Contacts To Access&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CONTACTS#Allow%20Contacts%20To%20Access'&gt;prefs:root=CONTACTS#Allow%20Contacts%20To%20Access&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Siri&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CONTACTS&amp;amp;path=SIRI_AND_SEARCH'&gt;prefs:root=CONTACTS&amp;amp;path=SIRI_AND_SEARCH&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Sort Order&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CONTACTS&amp;amp;path=ContactsSortOrder'&gt;prefs:root=CONTACTS&amp;amp;path=ContactsSortOrder&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Display Order&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CONTACTS&amp;amp;path=PersonNameOrder'&gt;prefs:root=CONTACTS&amp;amp;path=PersonNameOrder&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Short Name&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CONTACTS&amp;amp;path=PersonShortName'&gt;prefs:root=CONTACTS&amp;amp;path=PersonShortName&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Short Name&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CONTACTS&amp;amp;path=PersonShortName#Short%20Name'&gt;prefs:root=CONTACTS&amp;amp;path=PersonShortName#Short%20Name&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Prefer Nicknames&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CONTACTS&amp;amp;path=PersonShortName#Prefer%20Nicknames'&gt;prefs:root=CONTACTS&amp;amp;path=PersonShortName#Prefer%20Nicknames&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;My Info&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CONTACTS&amp;amp;path=MeCard'&gt;prefs:root=CONTACTS&amp;amp;path=MeCard&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Import SIM Contacts&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CONTACTS#SIMImport'&gt;prefs:root=CONTACTS#SIMImport&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Game Center&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=GAMECENTER'&gt;prefs:root=GAMECENTER&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Books&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=IBOOKS'&gt;prefs:root=IBOOKS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Online Content&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=IBOOKS#BKAllowOnlineContent'&gt;prefs:root=IBOOKS#BKAllowOnlineContent&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;SYNCING&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=IBOOKS#SYNCING'&gt;prefs:root=IBOOKS#SYNCING&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Reading Now&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=IBOOKS#BKLibrary.ReadingNow'&gt;prefs:root=IBOOKS#BKLibrary.ReadingNow&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;iCloud Drive&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=IBOOKS#BCSyncICloudDrive'&gt;prefs:root=IBOOKS#BCSyncICloudDrive&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;READING&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=IBOOKS#READING'&gt;prefs:root=IBOOKS#READING&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Full Justification&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=IBOOKS#BKFullJustification'&gt;prefs:root=IBOOKS#BKFullJustification&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Auto-hyphenation&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=IBOOKS#BKAutoHyphenation'&gt;prefs:root=IBOOKS#BKAutoHyphenation&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Both Margins Advance&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=IBOOKS#BKLeftTapTurnToNext'&gt;prefs:root=IBOOKS#BKLeftTapTurnToNext&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Reading Goals&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=IBOOKS#READING_GOALS'&gt;prefs:root=IBOOKS#READING_GOALS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Clear Reading Goals Data&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=IBOOKS#BKReadingGoalsShouldClearDataKey'&gt;prefs:root=IBOOKS#BKReadingGoalsShouldClearDataKey&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;SEARCHING&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=IBOOKS#SEARCHING'&gt;prefs:root=IBOOKS#SEARCHING&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Book Store&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=IBOOKS#BKIncludeBookStoreResultsInSearch'&gt;prefs:root=IBOOKS#BKIncludeBookStoreResultsInSearch&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;AUDIOBOOKS&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=IBOOKS#AUDIOBOOKS'&gt;prefs:root=IBOOKS#AUDIOBOOKS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Skip Forward&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=IBOOKS&amp;amp;path=BKAudioBookSkipForward'&gt;prefs:root=IBOOKS&amp;amp;path=BKAudioBookSkipForward&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Skip Back&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=IBOOKS&amp;amp;path=BKAudioBookSkipBackward'&gt;prefs:root=IBOOKS&amp;amp;path=BKAudioBookSkipBackward&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;EXTERNAL CONTROLS&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=IBOOKS#BKRemoteSkipInsteadOfNextTrackDefaultKey'&gt;prefs:root=IBOOKS#BKRemoteSkipInsteadOfNextTrackDefaultKey&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Next/Previous&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=IBOOKS#Next/Previous'&gt;prefs:root=IBOOKS#Next/Previous&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Skip Forward/Back&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=IBOOKS#Skip%20Forward/Back'&gt;prefs:root=IBOOKS#Skip%20Forward/Back&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Privacy&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=IBOOKS#PRIVACY'&gt;prefs:root=IBOOKS#PRIVACY&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Reset Identifier&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=IBOOKS#BAResetAnalyticsUserID'&gt;prefs:root=IBOOKS#BAResetAnalyticsUserID&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Acknowledgements&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=IBOOKS&amp;amp;path=Acknowledgements'&gt;prefs:root=IBOOKS&amp;amp;path=Acknowledgements&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Keyboard&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=Keyboard'&gt;prefs:root=General&amp;amp;path=Keyboard&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Keyboards&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=Keyboard/KEYBOARDS'&gt;prefs:root=General&amp;amp;path=Keyboard/KEYBOARDS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Text Replacement&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=Keyboard/USER_DICTIONARY'&gt;prefs:root=General&amp;amp;path=Keyboard/USER_DICTIONARY&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Auto-Capitalization&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=Keyboard#KeyboardAutocapitalization'&gt;prefs:root=General&amp;amp;path=Keyboard#KeyboardAutocapitalization&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Auto-Correction&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=Keyboard#KeyboardAutocorrection'&gt;prefs:root=General&amp;amp;path=Keyboard#KeyboardAutocorrection&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Check Spelling&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=Keyboard#KeyboardCheckSpelling'&gt;prefs:root=General&amp;amp;path=Keyboard#KeyboardCheckSpelling&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Enable Caps Lock&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=Keyboard#KeyboardCapsLock'&gt;prefs:root=General&amp;amp;path=Keyboard#KeyboardCapsLock&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Shortcuts&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=Keyboard#KeyboardAssistant'&gt;prefs:root=General&amp;amp;path=Keyboard#KeyboardAssistant&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Predictive&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=Keyboard#KeyboardPrediction'&gt;prefs:root=General&amp;amp;path=Keyboard#KeyboardPrediction&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Smart Punctuation&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=Keyboard#SmartTyping'&gt;prefs:root=General&amp;amp;path=Keyboard#SmartTyping&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Split Keyboard&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=Keyboard#RivenKeyboard'&gt;prefs:root=General&amp;amp;path=Keyboard#RivenKeyboard&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Enable Key Flicks&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=Keyboard#GesturesEnabled'&gt;prefs:root=General&amp;amp;path=Keyboard#GesturesEnabled&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Character Preview&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=Keyboard#KeyboardAllowPaddle'&gt;prefs:root=General&amp;amp;path=Keyboard#KeyboardAllowPaddle&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;“.” Shortcut&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=Keyboard#KeyboardPeriodShortcut'&gt;prefs:root=General&amp;amp;path=Keyboard#KeyboardPeriodShortcut&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Slide to type&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=Keyboard#KeyboardContinuousPathEnabled'&gt;prefs:root=General&amp;amp;path=Keyboard#KeyboardContinuousPathEnabled&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Enable Dictation&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=Keyboard#Dictation'&gt;prefs:root=General&amp;amp;path=Keyboard#Dictation&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Auto-Punctuation&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=Keyboard#AutoPunctuationSetting'&gt;prefs:root=General&amp;amp;path=Keyboard#AutoPunctuationSetting&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Calendar&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CALENDAR'&gt;prefs:root=CALENDAR&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Time Zone Override&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CALENDAR&amp;amp;path=TimeZoneCityArray'&gt;prefs:root=CALENDAR&amp;amp;path=TimeZoneCityArray&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Alternate Calendars&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CALENDAR&amp;amp;path=Alternate%20Calendars'&gt;prefs:root=CALENDAR&amp;amp;path=Alternate%20Calendars&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Week Numbers&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CALENDAR#Week%20Numbers'&gt;prefs:root=CALENDAR#Week%20Numbers&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Show Invitee Declines&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CALENDAR#Show%20Invitee%20Declines'&gt;prefs:root=CALENDAR#Show%20Invitee%20Declines&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Sync&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CALENDAR&amp;amp;path=Sync'&gt;prefs:root=CALENDAR&amp;amp;path=Sync&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Default Alert Times&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CALENDAR&amp;amp;path=Default%20Alert%20Times'&gt;prefs:root=CALENDAR&amp;amp;path=Default%20Alert%20Times&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Start Week On&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CALENDAR&amp;amp;path=Start%20Week%20On'&gt;prefs:root=CALENDAR&amp;amp;path=Start%20Week%20On&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Location Suggestions&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CALENDAR#Location%20Suggestions'&gt;prefs:root=CALENDAR#Location%20Suggestions&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Accounts&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL&amp;amp;path=ACCOUNTS'&gt;prefs:root=MAIL&amp;amp;path=ACCOUNTS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Add Account&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL&amp;amp;path=ACCOUNTS#ADD_ACCOUNT'&gt;prefs:root=MAIL&amp;amp;path=ACCOUNTS#ADD_ACCOUNT&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Fetch New Data&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL&amp;amp;path=ACCOUNTS#FETCH_NEW_DATA'&gt;prefs:root=MAIL&amp;amp;path=ACCOUNTS#FETCH_NEW_DATA&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Mail&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL'&gt;prefs:root=MAIL&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Preview&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL&amp;amp;path=Preview'&gt;prefs:root=MAIL&amp;amp;path=Preview&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Show To/Cc Labels&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL#Show%20To/Cc%20Labels'&gt;prefs:root=MAIL#Show%20To/Cc%20Labels&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Swipe Options&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL&amp;amp;path=Swipe%20Options'&gt;prefs:root=MAIL&amp;amp;path=Swipe%20Options&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Ask Before Deleting&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL#Ask%20Before%20Deleting'&gt;prefs:root=MAIL#Ask%20Before%20Deleting&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Load Remote Images&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL#Load%20Remote%20Images'&gt;prefs:root=MAIL#Load%20Remote%20Images&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Organize by Thread&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL#Organize%20by%20Thread'&gt;prefs:root=MAIL#Organize%20by%20Thread&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Collapse Read Messages&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL#Collapse%20Read%20Messages'&gt;prefs:root=MAIL#Collapse%20Read%20Messages&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Most Recent Message on Top&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL#Most%20Recent%20Message%20on%20Top'&gt;prefs:root=MAIL#Most%20Recent%20Message%20on%20Top&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Complete Threads&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL#Complete%20Threads'&gt;prefs:root=MAIL#Complete%20Threads&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Muted Thread Action&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL&amp;amp;path=Muted%20Thread%20Action'&gt;prefs:root=MAIL&amp;amp;path=Muted%20Thread%20Action&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Ignore Blocked Senders&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL#Ignore%20Blocked%20Senders'&gt;prefs:root=MAIL#Ignore%20Blocked%20Senders&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Blocked Sender Options&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL&amp;amp;path=Blocked%20Sender%20Options'&gt;prefs:root=MAIL&amp;amp;path=Blocked%20Sender%20Options&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Blocked&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL&amp;amp;path=Blocked'&gt;prefs:root=MAIL&amp;amp;path=Blocked&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Always Bcc Myself&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL#Always%20Bcc%20Myself'&gt;prefs:root=MAIL#Always%20Bcc%20Myself&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Mark Addresses&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL&amp;amp;path=Mark%20Addresses'&gt;prefs:root=MAIL&amp;amp;path=Mark%20Addresses&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Increase Quote Level&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL&amp;amp;path=Increase%20Quote%20Level'&gt;prefs:root=MAIL&amp;amp;path=Increase%20Quote%20Level&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Include Attachments with Replies&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL&amp;amp;path=Include%20Attachments%20with%20Replies'&gt;prefs:root=MAIL&amp;amp;path=Include%20Attachments%20with%20Replies&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Signature&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL&amp;amp;path=Signature'&gt;prefs:root=MAIL&amp;amp;path=Signature&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Phone&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Phone'&gt;prefs:root=Phone&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;My Number&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Phone&amp;amp;path=My%20Number'&gt;prefs:root=Phone&amp;amp;path=My%20Number&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Incoming Calls&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Phone&amp;amp;path=INCOMING_CALL_STYLE'&gt;prefs:root=Phone&amp;amp;path=INCOMING_CALL_STYLE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Announce Calls&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Phone&amp;amp;path=ANNOUNCE_CALLS'&gt;prefs:root=Phone&amp;amp;path=ANNOUNCE_CALLS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;SMS/Call Reporting&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Phone&amp;amp;path=CLASSIFICATION_AND_REPORTING'&gt;prefs:root=Phone&amp;amp;path=CLASSIFICATION_AND_REPORTING&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Respond with Text&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Phone&amp;amp;path=Respond%20with%20Text'&gt;prefs:root=Phone&amp;amp;path=Respond%20with%20Text&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Call Forwarding&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Phone&amp;amp;path=Call%20Forwarding'&gt;prefs:root=Phone&amp;amp;path=Call%20Forwarding&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Call Forwarding&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Phone&amp;amp;path=Call%20Forwarding#idMasterOnOffSwitch'&gt;prefs:root=Phone&amp;amp;path=Call%20Forwarding#idMasterOnOffSwitch&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Call Waiting&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Phone&amp;amp;path=Call%20Waiting'&gt;prefs:root=Phone&amp;amp;path=Call%20Waiting&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Show My Caller ID&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Phone&amp;amp;path=Show%20My%20Caller%20ID'&gt;prefs:root=Phone&amp;amp;path=Show%20My%20Caller%20ID&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Show My Caller ID&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Phone&amp;amp;path=Show%20My%20Caller%20ID/Primary#Show%20My%20Caller%20ID'&gt;prefs:root=Phone&amp;amp;path=Show%20My%20Caller%20ID/Primary#Show%20My%20Caller%20ID&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Silence Unknown Callers&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Phone#SILENCE_CALLS'&gt;prefs:root=Phone#SILENCE_CALLS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Blocked Contacts&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Phone&amp;amp;path=SPECIFIER_IDENTIFIER_BLACKLIST'&gt;prefs:root=Phone&amp;amp;path=SPECIFIER_IDENTIFIER_BLACKLIST&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Dial Assist&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Phone&amp;amp;path=Dial%20Assist'&gt;prefs:root=Phone&amp;amp;path=Dial%20Assist&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Photos&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Photos'&gt;prefs:root=Photos&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;iCloud Photos&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Photos#iCloudPhotosSwitch'&gt;prefs:root=Photos#iCloudPhotosSwitch&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Optimize Storage&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Photos#iCloudOptimizeStorageOption'&gt;prefs:root=Photos#iCloudOptimizeStorageOption&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Download and Keep Originals&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Photos#iCloudKeepOriginalsOption'&gt;prefs:root=Photos#iCloudKeepOriginalsOption&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Shared Albums&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Photos#SharedStreamsSwitch'&gt;prefs:root=Photos#SharedStreamsSwitch&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Cellular Data&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Photos&amp;amp;path=CellularDataLinkList'&gt;prefs:root=Photos&amp;amp;path=CellularDataLinkList&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Autoplay Videos&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Photos#VideoAutoplaySwitch'&gt;prefs:root=Photos#VideoAutoplaySwitch&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;View Full HDR&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Photos#ImageModulationSwitch'&gt;prefs:root=Photos#ImageModulationSwitch&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Show Holiday Events&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Photos#MEMORIES_HOLIDAY_CALENDAR_EVENTS_SWITCH'&gt;prefs:root=Photos#MEMORIES_HOLIDAY_CALENDAR_EVENTS_SWITCH&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Transfer to Mac or PC&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Photos#TransferGroup'&gt;prefs:root=Photos#TransferGroup&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;App Store&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=STORE'&gt;prefs:root=STORE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Automatic Downloads&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=STORE#Automatic%20Downloads'&gt;prefs:root=STORE#Automatic%20Downloads&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;App Updates&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=STORE#App%20Updates'&gt;prefs:root=STORE#App%20Updates&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;App Downloads&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=STORE&amp;amp;path=App%20Downloads'&gt;prefs:root=STORE&amp;amp;path=App%20Downloads&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Video Autoplay&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=STORE&amp;amp;path=Video%20Autoplay'&gt;prefs:root=STORE&amp;amp;path=Video%20Autoplay&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;In-App Ratings &amp;amp; Reviews&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=STORE#In-App%20Ratings%20&amp;amp;%20Reviews'&gt;prefs:root=STORE#In-App%20Ratings%20&amp;amp;%20Reviews&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;News&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=NEWS'&gt;prefs:root=NEWS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Show Story Previews&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=NEWS#show_excerpt_mode'&gt;prefs:root=NEWS#show_excerpt_mode&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Restrict Stories in Today&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=NEWS#showStoriesFromFavoritesSpecifierID'&gt;prefs:root=NEWS#showStoriesFromFavoritesSpecifierID&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Privacy&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=NEWS#Privacy'&gt;prefs:root=NEWS#Privacy&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Reset Identifier&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=NEWS#reset_identifier'&gt;prefs:root=NEWS#reset_identifier&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Acknowledgements&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=NEWS&amp;amp;path=Acknowledgements'&gt;prefs:root=NEWS&amp;amp;path=Acknowledgements&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Airplane Mode&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ROOT#AIRPLANE_MODE'&gt;prefs:root=ROOT#AIRPLANE_MODE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Reminders&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=REMINDERS'&gt;prefs:root=REMINDERS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Default List&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=REMINDERS&amp;amp;path=DEFAULT_LIST'&gt;prefs:root=REMINDERS&amp;amp;path=DEFAULT_LIST&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Reminders&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=REMINDERS&amp;amp;path=DEFAULT_LIST#preferredDefaultListID'&gt;prefs:root=REMINDERS&amp;amp;path=DEFAULT_LIST#preferredDefaultListID&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Today Notification&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=REMINDERS#todayNotificationFireTime'&gt;prefs:root=REMINDERS#todayNotificationFireTime&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Show as Overdue&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=REMINDERS#showRemindersAsOverdue'&gt;prefs:root=REMINDERS#showRemindersAsOverdue&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Battery&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=BATTERY_USAGE'&gt;prefs:root=BATTERY_USAGE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Battery&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=BATTERY_USAGE'&gt;prefs:root=BATTERY_USAGE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Low Power Mode&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=BATTERY_USAGE#BATTERY_SAVER_MODE'&gt;prefs:root=BATTERY_USAGE#BATTERY_SAVER_MODE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Battery Health&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=BATTERY_USAGE#BATTERY_HEALTH_ID'&gt;prefs:root=BATTERY_USAGE#BATTERY_HEALTH_ID&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;WLAN&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=WIFI'&gt;prefs:root=WIFI&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Wi-Fi&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=WIFI'&gt;prefs:root=WIFI&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Bluetooth&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Bluetooth'&gt;prefs:root=Bluetooth&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;SEARCH&lt;/h3&gt;
&lt;p&gt;&lt;a href='settings-navigation://com.apple.Settings.Search'&gt;settings-navigation://com.apple.Settings.Search&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Storage&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=STORAGE_MGMT#MANAGE'&gt;prefs:root=General&amp;amp;path=STORAGE_MGMT#MANAGE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Offload Unused Apps&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;amp;path=STORAGE_MGMT#OFFLOAD'&gt;prefs:root=General&amp;amp;path=STORAGE_MGMT#OFFLOAD&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Shortcuts&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SHORTCUTS'&gt;prefs:root=SHORTCUTS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;iCloud Sync&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SHORTCUTS#WFCloudKitSyncEnabled'&gt;prefs:root=SHORTCUTS#WFCloudKitSyncEnabled&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Sync Shortcut Order&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SHORTCUTS#WFCloudKitSyncOrderEnabled'&gt;prefs:root=SHORTCUTS#WFCloudKitSyncOrderEnabled&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Legal Notices&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SHORTCUTS&amp;amp;path=Legal%20Notices'&gt;prefs:root=SHORTCUTS&amp;amp;path=Legal%20Notices&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Siri&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SIRI'&gt;prefs:root=SIRI&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Allow Siri When Locked&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SIRI#ASSISTANT_LOCK_SCREEN_ACCESS'&gt;prefs:root=SIRI#ASSISTANT_LOCK_SCREEN_ACCESS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Language&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SIRI&amp;amp;path=LANGUAGE_ID'&gt;prefs:root=SIRI&amp;amp;path=LANGUAGE_ID&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Siri Voice&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SIRI&amp;amp;path=VOICE_ID'&gt;prefs:root=SIRI&amp;amp;path=VOICE_ID&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Voice Feedback&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SIRI&amp;amp;path=VOICE_FEEDBACK_ID'&gt;prefs:root=SIRI&amp;amp;path=VOICE_FEEDBACK_ID&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;My Information&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SIRI&amp;amp;path=MY_INFO'&gt;prefs:root=SIRI&amp;amp;path=MY_INFO&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Stocks&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=STOCKS'&gt;prefs:root=STOCKS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Privacy&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=STOCKS#Privacy'&gt;prefs:root=STOCKS#Privacy&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Reset Identifier&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=STOCKS#reset_identifier'&gt;prefs:root=STOCKS#reset_identifier&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Cellular&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MOBILE_DATA_SETTINGS_ID'&gt;prefs:root=MOBILE_DATA_SETTINGS_ID&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Cellular Data Options&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MOBILE_DATA_SETTINGS_ID&amp;amp;path=CELLULAR_DATA_OPTIONS'&gt;prefs:root=MOBILE_DATA_SETTINGS_ID&amp;amp;path=CELLULAR_DATA_OPTIONS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Low Data Mode&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MOBILE_DATA_SETTINGS_ID&amp;amp;path=CELLULAR_DATA_OPTIONS#Low%20Data%20Mode'&gt;prefs:root=MOBILE_DATA_SETTINGS_ID&amp;amp;path=CELLULAR_DATA_OPTIONS#Low%20Data%20Mode&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Cellular Data&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MOBILE_DATA_SETTINGS_ID&amp;amp;path=SHOW_ALL'&gt;prefs:root=MOBILE_DATA_SETTINGS_ID&amp;amp;path=SHOW_ALL&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;TV&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=TVAPP'&gt;prefs:root=TVAPP&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Videos&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=TVAPP'&gt;prefs:root=TVAPP&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Use Cellular Data for Playback&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=TVAPP#com.apple.videos%3AVideosUseCellularDataEnabledSetting'&gt;prefs:root=TVAPP#com.apple.videos%3AVideosUseCellularDataEnabledSetting&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Use Cellular Data for Playback&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=TVAPP#com.apple.videos%3AVideosUseCellularDataEnabledSetting'&gt;prefs:root=TVAPP#com.apple.videos%3AVideosUseCellularDataEnabledSetting&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Playback Quality&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=TVAPP#com.apple.videos%3APlaybackQualityGroup'&gt;prefs:root=TVAPP#com.apple.videos%3APlaybackQualityGroup&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Playback Quality&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=TVAPP#com.apple.videos%3APlaybackQualityGroup'&gt;prefs:root=TVAPP#com.apple.videos%3APlaybackQualityGroup&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Purchases and Rentals&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=TVAPP&amp;amp;path=com.apple.videos%3APreferredPurchaseResolution'&gt;prefs:root=TVAPP&amp;amp;path=com.apple.videos%3APreferredPurchaseResolution&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Purchases and Rentals&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=TVAPP&amp;amp;path=com.apple.videos%3APreferredPurchaseResolution'&gt;prefs:root=TVAPP&amp;amp;path=com.apple.videos%3APreferredPurchaseResolution&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Home Sharing&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=TVAPP#com.apple.videos%3AHomeSharingFooter'&gt;prefs:root=TVAPP#com.apple.videos%3AHomeSharingFooter&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Wallpaper&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Wallpaper'&gt;prefs:root=Wallpaper&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;iOS 18 (20-AUG-2024, 17:00 CEST)&lt;/summary&gt;
# Apple ID
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=APPLE_ACCOUNT'&gt;prefs:root=APPLE_ACCOUNT&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Name, Phone Numbers, Email&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=APPLE_ACCOUNT&amp;path=APPLE_ACCOUNT_CONTACT'&gt;prefs:root=APPLE_ACCOUNT&amp;amp;path=APPLE_ACCOUNT_CONTACT&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Password &amp;amp; Security&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=APPLE_ACCOUNT&amp;path=PASSWORD_AND_SECURITY'&gt;prefs:root=APPLE_ACCOUNT&amp;amp;path=PASSWORD_AND_SECURITY&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Payment &amp;amp; Shipping&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=APPLE_ACCOUNT&amp;path=PAYMENT_AND_SHIPPING'&gt;prefs:root=APPLE_ACCOUNT&amp;amp;path=PAYMENT_AND_SHIPPING&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Subscriptions&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=APPLE_ACCOUNT&amp;path=SUBSCRIPTIONS'&gt;prefs:root=APPLE_ACCOUNT&amp;amp;path=SUBSCRIPTIONS&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;iCloud&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CASTLE or prefs:root=APPLE_ACCOUNT&amp;path=ICLOUD_SERVICE'&gt;prefs:root=CASTLE or prefs:root=APPLE_ACCOUNT&amp;amp;path=ICLOUD_SERVICE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Keychain&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CASTLE&amp;path=com.apple.Dataclass.KeychainSync or prefs:root=APPLE_ACCOUNT&amp;path=ICLOUD_SERVICE/com.apple.Dataclass.KeychainSync'&gt;prefs:root=CASTLE&amp;amp;path=com.apple.Dataclass.KeychainSync or prefs:root=APPLE_ACCOUNT&amp;amp;path=ICLOUD_SERVICE/com.apple.Dataclass.KeychainSync&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;iCloud Backup&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CASTLE&amp;path=BACKUP or prefs:root=APPLE_ACCOUNT&amp;path=ICLOUD_SERVICE/BACKUP'&gt;prefs:root=CASTLE&amp;amp;path=BACKUP or prefs:root=APPLE_ACCOUNT&amp;amp;path=ICLOUD_SERVICE/BACKUP&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Find My&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=APPLE_ACCOUNT&amp;path=LOCATION_SHARING'&gt;prefs:root=APPLE_ACCOUNT&amp;amp;path=LOCATION_SHARING&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Family Sharing&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=APPLE_ACCOUNT&amp;path=FAMILY'&gt;prefs:root=APPLE_ACCOUNT&amp;amp;path=FAMILY&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Hide My Email&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=APPLE_ACCOUNT&amp;path=ICLOUD_SERVICE/PRIVATE_EMAIL_MANAGE'&gt;prefs:root=APPLE_ACCOUNT&amp;amp;path=ICLOUD_SERVICE/PRIVATE_EMAIL_MANAGE&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Wi-Fi&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=WIFI'&gt;prefs:root=WIFI&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Bluetooth&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Bluetooth'&gt;prefs:root=Bluetooth&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Cellular&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MOBILE_DATA_SETTINGS_ID'&gt;prefs:root=MOBILE_DATA_SETTINGS_ID&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Cellular Data Options&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MOBILE_DATA_SETTINGS_ID&amp;path=CELLULAR_DATA_OPTIONS'&gt;prefs:root=MOBILE_DATA_SETTINGS_ID&amp;amp;path=CELLULAR_DATA_OPTIONS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Cellular Data (for devices with two SIMs)&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MOBILE_DATA_SETTINGS_ID&amp;path=MOBILE_DATA_SETTINGS'&gt;prefs:root=MOBILE_DATA_SETTINGS_ID&amp;amp;path=MOBILE_DATA_SETTINGS&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Personal Hotspot&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MOBILE_DATA_SETTINGS_ID&amp;path=INTERNET_TETHERING'&gt;prefs:root=MOBILE_DATA_SETTINGS_ID&amp;amp;path=INTERNET_TETHERING&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Wi-Fi Password&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MOBILE_DATA_SETTINGS_ID&amp;path=INTERNET_TETHERING/Wi-Fi%20Password'&gt;prefs:root=MOBILE_DATA_SETTINGS_ID&amp;amp;path=INTERNET_TETHERING/Wi-Fi%20Password&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Family Sharing&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MOBILE_DATA_SETTINGS_ID&amp;path=INTERNET_TETHERING/Family%20Sharing'&gt;prefs:root=MOBILE_DATA_SETTINGS_ID&amp;amp;path=INTERNET_TETHERING/Family%20Sharing&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;[Family Member Name]&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MOBILE_DATA_SETTINGS_ID&amp;path=INTERNET_TETHERING/Family%20Sharing/[URL-encoded Family Member Name]'&gt;prefs:root=MOBILE_DATA_SETTINGS_ID&amp;amp;path=INTERNET_TETHERING/Family%20Sharing/[URL-encoded Family Member Name]&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;System Services&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MOBILE_DATA_SETTINGS_ID&amp;path=System%20Services'&gt;prefs:root=MOBILE_DATA_SETTINGS_ID&amp;amp;path=System%20Services&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;[plan name]&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MOBILE_DATA_SETTINGS_ID&amp;path=[plan name]'&gt;prefs:root=MOBILE_DATA_SETTINGS_ID&amp;amp;path=[plan name]&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Reset Statistics&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MOBILE_DATA_SETTINGS_ID#Reset%20Statistics'&gt;prefs:root=MOBILE_DATA_SETTINGS_ID#Reset%20Statistics&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Personal Hotspot&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=INTERNET_TETHERING'&gt;prefs:root=INTERNET_TETHERING&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Wi-Fi Password&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=INTERNET_TETHERING&amp;path=Wi-Fi%20Password'&gt;prefs:root=INTERNET_TETHERING&amp;amp;path=Wi-Fi%20Password&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Family Sharing&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=INTERNET_TETHERING&amp;path=Family%20Sharing'&gt;prefs:root=INTERNET_TETHERING&amp;amp;path=Family%20Sharing&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;[Family Member Name]&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=INTERNET_TETHERING&amp;path=Family%20Sharing/[URL-encoded Family Member Name]'&gt;prefs:root=INTERNET_TETHERING&amp;amp;path=Family%20Sharing/[URL-encoded Family Member Name]&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;VPN&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=VPN'&gt;prefs:root=VPN&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;[VPN configuration]&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=VPN&amp;path=[URL-encoded VPN Configuration Name]'&gt;prefs:root=VPN&amp;amp;path=[URL-encoded VPN Configuration Name]&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Delete VPN&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=VPN&amp;path=[URL-encoded VPN Configuration Name]/Delete%20VPN'&gt;prefs:root=VPN&amp;amp;path=[URL-encoded VPN Configuration Name]/Delete%20VPN&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Add VPN Configuration…&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=VPN&amp;path=Add%20VPN%20Configuration%E2%80%A6'&gt;prefs:root=VPN&amp;amp;path=Add%20VPN%20Configuration%E2%80%A6&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Notifications&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=NOTIFICATIONS_ID'&gt;prefs:root=NOTIFICATIONS_ID&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;App Name&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=NOTIFICATIONS_ID&amp;path=App%20Bundle%20ID'&gt;prefs:root=NOTIFICATIONS_ID&amp;amp;path=App%20Bundle%20ID&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Show Previews&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=NOTIFICATIONS_ID&amp;path=SHOW_PREVIEW_GROUP_ID (credit to'&gt;prefs:root=NOTIFICATIONS_ID&amp;amp;path=SHOW_PREVIEW_GROUP_ID (credit to&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Siri Suggestions&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=NOTIFICATIONS_ID&amp;path=Siri%20Suggestions'&gt;prefs:root=NOTIFICATIONS_ID&amp;amp;path=Siri%20Suggestions&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Sounds&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Sounds'&gt;prefs:root=Sounds&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Ringtone&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Sounds&amp;path=Ringtone'&gt;prefs:root=Sounds&amp;amp;path=Ringtone&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Text Tone&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Sounds&amp;path=Text_Messages'&gt;prefs:root=Sounds&amp;amp;path=Text_Messages&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;New Voicemail&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Sounds&amp;path=Voicemail'&gt;prefs:root=Sounds&amp;amp;path=Voicemail&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;New Mail&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Sounds&amp;path=NEW_MAIL'&gt;prefs:root=Sounds&amp;amp;path=NEW_MAIL&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Sent Mail&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Sounds&amp;path=SENT_MAIL'&gt;prefs:root=Sounds&amp;amp;path=SENT_MAIL&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Calendar Alerts&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Sounds&amp;path=Calendar%20Alarm'&gt;prefs:root=Sounds&amp;amp;path=Calendar%20Alarm&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Reminder Alerts&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Sounds&amp;path=Reminder%20Alerts'&gt;prefs:root=Sounds&amp;amp;path=Reminder%20Alerts&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;AirDrop&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Sounds&amp;path=AIRDROP'&gt;prefs:root=Sounds&amp;amp;path=AIRDROP&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Do Not Disturb&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DO_NOT_DISTURB'&gt;prefs:root=DO_NOT_DISTURB&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Allow Calls From&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DO_NOT_DISTURB&amp;path=Allow%20Calls%20From'&gt;prefs:root=DO_NOT_DISTURB&amp;amp;path=Allow%20Calls%20From&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Auto-Reply To&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DO_NOT_DISTURB&amp;path=DRIVER_MODE_AUTOREPLY'&gt;prefs:root=DO_NOT_DISTURB&amp;amp;path=DRIVER_MODE_AUTOREPLY&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Auto-Reply&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DO_NOT_DISTURB&amp;path=DRIVER_MODE_AUTOREPLY_MESSAGE'&gt;prefs:root=DO_NOT_DISTURB&amp;amp;path=DRIVER_MODE_AUTOREPLY_MESSAGE&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Screen Time&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SCREEN_TIME'&gt;prefs:root=SCREEN_TIME&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Turn On Screen Time&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SCREEN_TIME&amp;path=Turn%20On%20Screen%20Time'&gt;prefs:root=SCREEN_TIME&amp;amp;path=Turn%20On%20Screen%20Time&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;See All Activity&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SCREEN_TIME&amp;path=SCREEN_TIME_SUMMARY'&gt;prefs:root=SCREEN_TIME&amp;amp;path=SCREEN_TIME_SUMMARY&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Downtime&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SCREEN_TIME&amp;path=DOWNTIME'&gt;prefs:root=SCREEN_TIME&amp;amp;path=DOWNTIME&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;App Limits&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SCREEN_TIME&amp;path=APP_LIMITS'&gt;prefs:root=SCREEN_TIME&amp;amp;path=APP_LIMITS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Always Allowed&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SCREEN_TIME&amp;path=ALWAYS_ALLOWED'&gt;prefs:root=SCREEN_TIME&amp;amp;path=ALWAYS_ALLOWED&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Communication Limits&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SCREEN_TIME&amp;path=COMMUNICATION_LIMITS'&gt;prefs:root=SCREEN_TIME&amp;amp;path=COMMUNICATION_LIMITS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Content &amp;amp; Privacy Restrictions&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SCREEN_TIME&amp;path=CONTENT_PRIVACY'&gt;prefs:root=SCREEN_TIME&amp;amp;path=CONTENT_PRIVACY&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;General&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General'&gt;prefs:root=General&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;About&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;path=About'&gt;prefs:root=General&amp;amp;path=About&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;SEID&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;path=About/SEID'&gt;prefs:root=General&amp;amp;path=About/SEID&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Certificate Trust Settings&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;path=About/CERT_TRUST_SETTINGS'&gt;prefs:root=General&amp;amp;path=About/CERT_TRUST_SETTINGS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Software Update&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;path=SOFTWARE_UPDATE_LINK'&gt;prefs:root=General&amp;amp;path=SOFTWARE_UPDATE_LINK&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;AirDrop&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;path=AIRDROP_LINK'&gt;prefs:root=General&amp;amp;path=AIRDROP_LINK&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;AirPlay &amp;amp; Handoff&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;path=CONTINUITY_SPEC'&gt;prefs:root=General&amp;amp;path=CONTINUITY_SPEC&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;CarPlay&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;path=CARPLAY'&gt;prefs:root=General&amp;amp;path=CARPLAY&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Storage&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;path=STORAGE_MGMT'&gt;prefs:root=General&amp;amp;path=STORAGE_MGMT&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;AppName&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;path=STORAGE_MGMT/[App Bundle ID]'&gt;prefs:root=General&amp;amp;path=STORAGE_MGMT/[App Bundle ID]&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Background App Refresh&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;path=AUTO_CONTENT_DOWNLOAD'&gt;prefs:root=General&amp;amp;path=AUTO_CONTENT_DOWNLOAD&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Background App Refresh&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;path=AUTO_CONTENT_DOWNLOAD/AUTO_CONTENT_DOWNLOAD'&gt;prefs:root=General&amp;amp;path=AUTO_CONTENT_DOWNLOAD/AUTO_CONTENT_DOWNLOAD&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Date &amp;amp; Time&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;path=DATE_AND_TIME'&gt;prefs:root=General&amp;amp;path=DATE_AND_TIME&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Keyboard&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;path=Keyboard'&gt;prefs:root=General&amp;amp;path=Keyboard&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Keyboards&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;path=Keyboard/KEYBOARDS'&gt;prefs:root=General&amp;amp;path=Keyboard/KEYBOARDS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Hardware Keyboard&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;path=Keyboard/Hardware%20Keyboard'&gt;prefs:root=General&amp;amp;path=Keyboard/Hardware%20Keyboard&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Text Replacement&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;path=Keyboard/USER_DICTIONARY'&gt;prefs:root=General&amp;amp;path=Keyboard/USER_DICTIONARY&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;One Handed Keyboard&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;path=Keyboard/ReachableKeyboard'&gt;prefs:root=General&amp;amp;path=Keyboard/ReachableKeyboard&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Language &amp;amp; Region&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;path=INTERNATIONAL'&gt;prefs:root=General&amp;amp;path=INTERNATIONAL&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Device Language&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;path=INTERNATIONAL/DEVICE_LANGUAGE'&gt;prefs:root=General&amp;amp;path=INTERNATIONAL/DEVICE_LANGUAGE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Region&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;path=INTERNATIONAL/LOCALE'&gt;prefs:root=General&amp;amp;path=INTERNATIONAL/LOCALE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Calendar&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;path=INTERNATIONAL/CALENDAR'&gt;prefs:root=General&amp;amp;path=INTERNATIONAL/CALENDAR&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Numbers&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;path=INTERNATIONAL/NUMBERING_SYSTEM'&gt;prefs:root=General&amp;amp;path=INTERNATIONAL/NUMBERING_SYSTEM&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Temperature Unit&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;path=INTERNATIONAL/TEMPERATURE_UNIT'&gt;prefs:root=General&amp;amp;path=INTERNATIONAL/TEMPERATURE_UNIT&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Dictionary&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;path=DICTIONARY'&gt;prefs:root=General&amp;amp;path=DICTIONARY&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;VPN&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;path=VPN'&gt;prefs:root=General&amp;amp;path=VPN&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;[VPN Configuration Name]&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;path=VPN/[URL-encoded VPN Configuration Name]'&gt;prefs:root=General&amp;amp;path=VPN/[URL-encoded VPN Configuration Name]&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Delete VPN&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;path=VPN/[URL-encoded VPN Configuration Name]/Delete%20VPN'&gt;prefs:root=General&amp;amp;path=VPN/[URL-encoded VPN Configuration Name]/Delete%20VPN&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Add VPN Configuration…&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;path=VPN/Add%20VPN%20Configuration%E2%80%A6'&gt;prefs:root=General&amp;amp;path=VPN/Add%20VPN%20Configuration%E2%80%A6&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;DNS&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;path=VPN/DNS'&gt;prefs:root=General&amp;amp;path=VPN/DNS&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Profiles&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;path=ManagedConfigurationList'&gt;prefs:root=General&amp;amp;path=ManagedConfigurationList&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Install Profile&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;path=ManagedConfigurationList/PurgatoryInstallRequested'&gt;prefs:root=General&amp;amp;path=ManagedConfigurationList/PurgatoryInstallRequested&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Legal &amp;amp; Regulatory&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;path=LEGAL_AND_REGULATORY'&gt;prefs:root=General&amp;amp;path=LEGAL_AND_REGULATORY&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Reset&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=General&amp;path=Reset'&gt;prefs:root=General&amp;amp;path=Reset&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Control Center&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ControlCenter'&gt;prefs:root=ControlCenter&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Customize Controls&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ControlCenter&amp;path=CUSTOMIZE_CONTROLS'&gt;prefs:root=ControlCenter&amp;amp;path=CUSTOMIZE_CONTROLS&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Action Button&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACTION_BUTTON'&gt;prefs:root=ACTION_BUTTON&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Display&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DISPLAY'&gt;prefs:root=DISPLAY&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Options&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DISPLAY&amp;path=APPEARANCE_OPTIONS'&gt;prefs:root=DISPLAY&amp;amp;path=APPEARANCE_OPTIONS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Night Shift&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DISPLAY&amp;path=BLUE_LIGHT_REDUCTION'&gt;prefs:root=DISPLAY&amp;amp;path=BLUE_LIGHT_REDUCTION&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Auto Lock&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DISPLAY&amp;path=AUTOLOCK'&gt;prefs:root=DISPLAY&amp;amp;path=AUTOLOCK&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Text Size&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DISPLAY&amp;path=TEXT_SIZE'&gt;prefs:root=DISPLAY&amp;amp;path=TEXT_SIZE&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Home Screen&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=HOME_SCREEN'&gt;prefs:root=HOME_SCREEN&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Home Screen &amp;amp; Dock&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=HOME_SCREEN_DOCK'&gt;prefs:root=HOME_SCREEN_DOCK&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Multitasking&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=HOME_SCREEN_DOCK&amp;path=MULTITASKING'&gt;prefs:root=HOME_SCREEN_DOCK&amp;amp;path=MULTITASKING&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Accessibility&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY'&gt;prefs:root=ACCESSIBILITY&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;VoiceOver&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=VOICEOVER_TITLE'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=VOICEOVER_TITLE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Speech&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=VOICEOVER_TITLE/SPEECH_TITLE'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=VOICEOVER_TITLE/SPEECH_TITLE&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Braille&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=VOICEOVER_TITLE/BRAILLE_TITLE'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=VOICEOVER_TITLE/BRAILLE_TITLE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Output&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=VOICEOVER_TITLE/BRAILLE_TITLE/BrailleDisplayOutput'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=VOICEOVER_TITLE/BRAILLE_TITLE/BrailleDisplayOutput&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Input&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=VOICEOVER_TITLE/BRAILLE_TITLE/BrailleDisplayInput'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=VOICEOVER_TITLE/BRAILLE_TITLE/BrailleDisplayInput&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Braille Screen Input&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=VOICEOVER_TITLE/BRAILLE_TITLE/BrailleGesturesInput'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=VOICEOVER_TITLE/BRAILLE_TITLE/BrailleGesturesInput&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Braille Tables&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=VOICEOVER_TITLE/BRAILLE_TITLE/tableIdentifier'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=VOICEOVER_TITLE/BRAILLE_TITLE/tableIdentifier&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Language&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=VOICEOVER_TITLE/BRAILLE_TITLE/tableIdentifier/DefaultLanguage'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=VOICEOVER_TITLE/BRAILLE_TITLE/tableIdentifier/DefaultLanguage&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Status Cells&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=VOICEOVER_TITLE/BRAILLE_TITLE/STATUS_CELL'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=VOICEOVER_TITLE/BRAILLE_TITLE/STATUS_CELL&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Verbosity&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=VOICEOVER_TITLE/VERBOSITY'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=VOICEOVER_TITLE/VERBOSITY&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Punctuation&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=VOICEOVER_TITLE/VERBOSITY/voiceOverPunctuationGroup'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=VOICEOVER_TITLE/VERBOSITY/voiceOverPunctuationGroup&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Media Descriptions&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=VOICEOVER_TITLE/VERBOSITY/voiceOverMediaDescriptions'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=VOICEOVER_TITLE/VERBOSITY/voiceOverMediaDescriptions&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Audio&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=VOICEOVER_TITLE/AUDIO_TITLE'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=VOICEOVER_TITLE/AUDIO_TITLE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Commands&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=VOICEOVER_TITLE/CUSTOMIZE_COMMANDS'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=VOICEOVER_TITLE/CUSTOMIZE_COMMANDS&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Activities&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=VOICEOVER_TITLE/ACTIVITIES'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=VOICEOVER_TITLE/ACTIVITIES&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Programming&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=VOICEOVER_TITLE/ACTIVITIES/Programming'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=VOICEOVER_TITLE/ACTIVITIES/Programming&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Add Activity…&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=VOICEOVER_TITLE/ACTIVITIES/New'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=VOICEOVER_TITLE/ACTIVITIES/New&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Rotor&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=VOICEOVER_TITLE/WEB_ROTOR'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=VOICEOVER_TITLE/WEB_ROTOR&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Rotor Actions&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=VOICEOVER_TITLE/ROTOR_ACTIONS'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=VOICEOVER_TITLE/ROTOR_ACTIONS&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Typing&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=VOICEOVER_TITLE/TYPING_OPTIONS'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=VOICEOVER_TITLE/TYPING_OPTIONS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Typing Style&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=VOICEOVER_TITLE/TYPING_OPTIONS/Typing%20Style'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=VOICEOVER_TITLE/TYPING_OPTIONS/Typing%20Style&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Phonetic Feedback&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=VOICEOVER_TITLE/TYPING_OPTIONS/Phonetic%20Feedback'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=VOICEOVER_TITLE/TYPING_OPTIONS/Phonetic%20Feedback&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Modifier Keys&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=VOICEOVER_TITLE/TYPING_OPTIONS/Modifier%20Keys'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=VOICEOVER_TITLE/TYPING_OPTIONS/Modifier%20Keys&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Keyboard Interaction Time&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=VOICEOVER_TITLE/TYPING_OPTIONS/Keyboard%20Interaction%20Time'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=VOICEOVER_TITLE/TYPING_OPTIONS/Keyboard%20Interaction%20Time&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Navigate Images&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=VOICEOVER_TITLE/INCLUDE_UNLABELED_IMAGES_TITLE'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=VOICEOVER_TITLE/INCLUDE_UNLABELED_IMAGES_TITLE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Double-tap Timeout&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=VOICEOVER_TITLE/DOUBLE_TAP_INTERVAL_TITLE'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=VOICEOVER_TITLE/DOUBLE_TAP_INTERVAL_TITLE&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Zoom&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=ZOOM_TITLE'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=ZOOM_TITLE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Keyboard Shortcuts&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=ZOOM_TITLE/ZoomKeyboardShortcuts'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=ZOOM_TITLE/ZoomKeyboardShortcuts&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Zoom Controller&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=ZOOM_TITLE/ZoomSlug'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=ZOOM_TITLE/ZoomSlug&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Zoom Filter&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=ZOOM_TITLE/ZoomFilter'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=ZOOM_TITLE/ZoomFilter&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Magnifier&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=MAGNIFIER_TITLE'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=MAGNIFIER_TITLE&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Display &amp;amp; Text Size&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=DISPLAY_AND_TEXT'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=DISPLAY_AND_TEXT&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Larger Text&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=DISPLAY_AND_TEXT/Larger%20Text'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=DISPLAY_AND_TEXT/Larger%20Text&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Color Filters&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=DISPLAY_AND_TEXT/DISPLAY_FILTER_COLOR'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=DISPLAY_AND_TEXT/DISPLAY_FILTER_COLOR&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Motion&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=MOTION_TITLE'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=MOTION_TITLE&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Spoken Content&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=SPEECH_TITLE'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=SPEECH_TITLE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Speech Controller&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=SPEECH_TITLE/SpeechController'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=SPEECH_TITLE/SpeechController&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Customize Mouse Buttons&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=SPEECH_TITLE/SpeechController/CustomizeMouseButtons'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=SPEECH_TITLE/SpeechController/CustomizeMouseButtons&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Highlight Content&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=SPEECH_TITLE/QuickSpeakHighlight'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=SPEECH_TITLE/QuickSpeakHighlight&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Typing Feedback&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=SPEECH_TITLE/TypingFeedback'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=SPEECH_TITLE/TypingFeedback&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Voices&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=SPEECH_TITLE/QuickSpeakAccents'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=SPEECH_TITLE/QuickSpeakAccents&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Audio Descriptions&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=DESCRIPTIVE_VIDEO'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=DESCRIPTIVE_VIDEO&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Touch&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=TOUCH_REACHABILITY_TITLE'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=TOUCH_REACHABILITY_TITLE&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;AssistiveTouch&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=TOUCH_REACHABILITY_TITLE/AIR_TOUCH_TITLE'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=TOUCH_REACHABILITY_TITLE/AIR_TOUCH_TITLE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Customize Top Level Menu&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=TOUCH_REACHABILITY_TITLE/AIR_TOUCH_TITLE/AssistiveTouchCustomize'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=TOUCH_REACHABILITY_TITLE/AIR_TOUCH_TITLE/AssistiveTouchCustomize&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Single-Tap&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=TOUCH_REACHABILITY_TITLE/AIR_TOUCH_TITLE/Single-Tap'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=TOUCH_REACHABILITY_TITLE/AIR_TOUCH_TITLE/Single-Tap&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Idle Opacity&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=TOUCH_REACHABILITY_TITLE/AIR_TOUCH_TITLE/IdleOpacity'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=TOUCH_REACHABILITY_TITLE/AIR_TOUCH_TITLE/IdleOpacity&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Devices&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=TOUCH_REACHABILITY_TITLE/AIR_TOUCH_TITLE/AssistiveTouchMouseDevices'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=TOUCH_REACHABILITY_TITLE/AIR_TOUCH_TITLE/AssistiveTouchMouseDevices&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Bluetooth Devices…&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=TOUCH_REACHABILITY_TITLE/AIR_TOUCH_TITLE/AssistiveTouchMouseDevices/Bluetooth%20Devices%E2%80%A6'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=TOUCH_REACHABILITY_TITLE/AIR_TOUCH_TITLE/AssistiveTouchMouseDevices/Bluetooth%20Devices%E2%80%A6&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Mouse Keys&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=TOUCH_REACHABILITY_TITLE/AIR_TOUCH_TITLE/AssistiveTouchMouseKeys'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=TOUCH_REACHABILITY_TITLE/AIR_TOUCH_TITLE/AssistiveTouchMouseKeys&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Pointer Style&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=TOUCH_REACHABILITY_TITLE/AIR_TOUCH_TITLE/ASTMousePointerCustomization'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=TOUCH_REACHABILITY_TITLE/AIR_TOUCH_TITLE/ASTMousePointerCustomization&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Color&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=TOUCH_REACHABILITY_TITLE/AIR_TOUCH_TITLE/ASTMousePointerCustomization/Color'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=TOUCH_REACHABILITY_TITLE/AIR_TOUCH_TITLE/ASTMousePointerCustomization/Color&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Customize Mouse Buttons&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=TOUCH_REACHABILITY_TITLE/AIR_TOUCH_TITLE/ASTMousePointerCustomization/Color/CustomizeMouseButtons'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=TOUCH_REACHABILITY_TITLE/AIR_TOUCH_TITLE/ASTMousePointerCustomization/Color/CustomizeMouseButtons&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Auto-Hide&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=TOUCH_REACHABILITY_TITLE/AIR_TOUCH_TITLE/ASTMousePointerCustomization/Auto-Hide'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=TOUCH_REACHABILITY_TITLE/AIR_TOUCH_TITLE/ASTMousePointerCustomization/Auto-Hide&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Movement Tolerance&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=TOUCH_REACHABILITY_TITLE/AIR_TOUCH_TITLE/Movement%20Tolerance'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=TOUCH_REACHABILITY_TITLE/AIR_TOUCH_TITLE/Movement%20Tolerance&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Force/Haptic Touch&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=TOUCH_REACHABILITY_TITLE/ForceTouch'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=TOUCH_REACHABILITY_TITLE/ForceTouch&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Touch Accommodations&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=TOUCH_REACHABILITY_TITLE/Touch%20Accommodations'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=TOUCH_REACHABILITY_TITLE/Touch%20Accommodations&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Call Audio Routing&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=TOUCH_REACHABILITY_TITLE/CALL_AUDIO_ROUTING'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=TOUCH_REACHABILITY_TITLE/CALL_AUDIO_ROUTING&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Auto-Answer Calls&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=TOUCH_REACHABILITY_TITLE/CALL_AUDIO_ROUTING/callAudioRoutingAutoAnswer'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=TOUCH_REACHABILITY_TITLE/CALL_AUDIO_ROUTING/callAudioRoutingAutoAnswer&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Back Tap&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=TOUCH_REACHABILITY_TITLE/Back%20Tap'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=TOUCH_REACHABILITY_TITLE/Back%20Tap&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Switch Control&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=ScannerSwitchTitle'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=ScannerSwitchTitle&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Switches&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=ScannerSwitchTitle/SwitchesIdentifier'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=ScannerSwitchTitle/SwitchesIdentifier&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Bluetooth Devices…&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=ScannerSwitchTitle/SwitchesIdentifier/BluetoothDevicesIdentifier'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=ScannerSwitchTitle/SwitchesIdentifier/BluetoothDevicesIdentifier&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Scanning Style&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=ScannerSwitchTitle/Scanning%20Style'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=ScannerSwitchTitle/Scanning%20Style&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Pause on First Item&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=ScannerSwitchTitle/Pause%20on%20First%20Item'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=ScannerSwitchTitle/Pause%20on%20First%20Item&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Move Repeat&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=ScannerSwitchTitle/Move%20Repeat'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=ScannerSwitchTitle/Move%20Repeat&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Long Press&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=ScannerSwitchTitle/Long%20Press'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=ScannerSwitchTitle/Long%20Press&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Tap Behavior&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=ScannerSwitchTitle/Tap%20Behavior'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=ScannerSwitchTitle/Tap%20Behavior&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Focused Item After Tap&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=ScannerSwitchTitle/Focused%20Item%20After%20Tap'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=ScannerSwitchTitle/Focused%20Item%20After%20Tap&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Hold Duration&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=ScannerSwitchTitle/Hold%20Duration'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=ScannerSwitchTitle/Hold%20Duration&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Ignore Repeat&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=ScannerSwitchTitle/Ignore%20Repeat'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=ScannerSwitchTitle/Ignore%20Repeat&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Gliding Cursor&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=ScannerSwitchTitle/Gliding%20Cursor'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=ScannerSwitchTitle/Gliding%20Cursor&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Head Tracking&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=ScannerSwitchTitle/CameraPointPickerSwitch'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=ScannerSwitchTitle/CameraPointPickerSwitch&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Speech&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=ScannerSwitchTitle/SpeechIdentifier'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=ScannerSwitchTitle/SpeechIdentifier&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Voices&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=ScannerSwitchTitle/SpeechIdentifier/VoicesIdentifier'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=ScannerSwitchTitle/SpeechIdentifier/VoicesIdentifier&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Menu Items&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=ScannerSwitchTitle/CustomizeMenuIdentifier'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=ScannerSwitchTitle/CustomizeMenuIdentifier&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Top Level&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=ScannerSwitchTitle/CustomizeMenuIdentifier/Top%20Level'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=ScannerSwitchTitle/CustomizeMenuIdentifier/Top%20Level&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Gestures&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=ScannerSwitchTitle/CustomizeMenuIdentifier/Gestures'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=ScannerSwitchTitle/CustomizeMenuIdentifier/Gestures&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Device&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=ScannerSwitchTitle/CustomizeMenuIdentifier/Device'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=ScannerSwitchTitle/CustomizeMenuIdentifier/Device&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Settings&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=ScannerSwitchTitle/CustomizeMenuIdentifier/Settings'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=ScannerSwitchTitle/CustomizeMenuIdentifier/Settings&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Media Controls&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=ScannerSwitchTitle/CustomizeMenuIdentifier/Media%20Controls'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=ScannerSwitchTitle/CustomizeMenuIdentifier/Media%20Controls&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Saved Gestures&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=ScannerSwitchTitle/CustomGesturesIdentifier'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=ScannerSwitchTitle/CustomGesturesIdentifier&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Voice Control&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=CommandAndControlTitle'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=CommandAndControlTitle&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Language&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=CommandAndControlTitle/LANGUAGE'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=CommandAndControlTitle/LANGUAGE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Customize Commands&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=CommandAndControlTitle/CUSTOMIZE_COMMANDS'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=CommandAndControlTitle/CUSTOMIZE_COMMANDS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Vocabulary&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=CommandAndControlTitle/VOCABULARY'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=CommandAndControlTitle/VOCABULARY&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Overlay&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=CommandAndControlTitle/ALWAYS_SHOW_OVERLAY'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=CommandAndControlTitle/ALWAYS_SHOW_OVERLAY&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Home Button&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=HOME_CLICK_TITLE'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=HOME_CLICK_TITLE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Apple TV Remote&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=Apple%20TV%20Remote'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=Apple%20TV%20Remote&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Keyboards&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=KEYBOARDS'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=KEYBOARDS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Full Keyboard Access&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=KEYBOARDS/Full%20Keyboard%20Access'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=KEYBOARDS/Full%20Keyboard%20Access&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Key Repeat&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=KEYBOARDS/KEY_REPEAT'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=KEYBOARDS/KEY_REPEAT&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Sticky Keys&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=KEYBOARDS/STICKY_KEYS'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=KEYBOARDS/STICKY_KEYS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Slow Keys&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=KEYBOARDS/SLOW_KEYS'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=KEYBOARDS/SLOW_KEYS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Hearing Devices&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=HEARING_AID_TITLE'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=HEARING_AID_TITLE&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Sound Recognition&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=SOUND_RECOGNITION_TITLE'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=SOUND_RECOGNITION_TITLE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Sounds&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=SOUND_RECOGNITION_TITLE/Sounds'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=SOUND_RECOGNITION_TITLE/Sounds&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;RTT&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=RTT'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=RTT&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Audio/Visual&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=AUDIO_VISUAL_TITLE'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=AUDIO_VISUAL_TITLE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Subtitles &amp;amp; Captioning&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=SUBTITLES_CAPTIONING'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=SUBTITLES_CAPTIONING&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Guided Access&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=GUIDED_ACCESS_TITLE'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=GUIDED_ACCESS_TITLE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Passcode Settings&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=GUIDED_ACCESS_TITLE/GuidedAccessSecurityLinkList'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=GUIDED_ACCESS_TITLE/GuidedAccessSecurityLinkList&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Time Limits&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=GUIDED_ACCESS_TITLE/GuidedAccessTimeRestrictionsLinkList'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=GUIDED_ACCESS_TITLE/GuidedAccessTimeRestrictionsLinkList&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Sound&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=GUIDED_ACCESS_TITLE/GuidedAccessTimeRestrictionsLinkList/GUIDED_ACCESS_TIME_RESTRICTIONS_SOUND_TITLE'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=GUIDED_ACCESS_TITLE/GuidedAccessTimeRestrictionsLinkList/GUIDED_ACCESS_TIME_RESTRICTIONS_SOUND_TITLE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Display Auto-Lock&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=GUIDED_ACCESS_TITLE/GuidedAccessAutoLockTime'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=GUIDED_ACCESS_TITLE/GuidedAccessAutoLockTime&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Siri&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=SIRI_SETTINGS_TITLE'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=SIRI_SETTINGS_TITLE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Accessibility Shortcut&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCESSIBILITY&amp;path=TRIPLE_CLICK_TITLE'&gt;prefs:root=ACCESSIBILITY&amp;amp;path=TRIPLE_CLICK_TITLE&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Wallpaper&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Wallpaper'&gt;prefs:root=Wallpaper&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Siri&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SIRI or prefs-siri-shortcuts:root'&gt;prefs:root=SIRI or prefs-siri-shortcuts:root&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Language&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SIRI&amp;path=LANGUAGE_ID'&gt;prefs:root=SIRI&amp;amp;path=LANGUAGE_ID&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Voice&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SIRI&amp;path=VOICE_ID'&gt;prefs:root=SIRI&amp;amp;path=VOICE_ID&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Siri Responses&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SIRI&amp;path=VOICE_FEEDBACK_ID'&gt;prefs:root=SIRI&amp;amp;path=VOICE_FEEDBACK_ID&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Siri &amp;amp; Dictation History&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SIRI&amp;path=HISTORY'&gt;prefs:root=SIRI&amp;amp;path=HISTORY&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;AppName&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SIRI&amp;path=[App Bundle ID]'&gt;prefs:root=SIRI&amp;amp;path=[App Bundle ID]&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Apple Pencil&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Pencil'&gt;prefs:root=Pencil&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Passcode&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=PASSCODE'&gt;prefs:root=PASSCODE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Require Passcode&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=PASSCODE&amp;path=PASSCODE_REQ'&gt;prefs:root=PASSCODE&amp;amp;path=PASSCODE_REQ&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Emergency SOS&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=EMERGENCY_SOS'&gt;prefs:root=EMERGENCY_SOS&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Exposure Notifications&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=EXPOSURE_NOTIFICATION'&gt;prefs:root=EXPOSURE_NOTIFICATION&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Battery&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=BATTERY_USAGE'&gt;prefs:root=BATTERY_USAGE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Battery Health&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=BATTERY_USAGE&amp;path=BATTERY_HEALTH'&gt;prefs:root=BATTERY_USAGE&amp;amp;path=BATTERY_HEALTH&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Privacy&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy'&gt;prefs:root=Privacy&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Location Services&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;path=LOCATION'&gt;prefs:root=Privacy&amp;amp;path=LOCATION&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Share My Location&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;path=LOCATION/LOCATION_SHARING'&gt;prefs:root=Privacy&amp;amp;path=LOCATION/LOCATION_SHARING&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;[Family Member Name]&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;path=LOCATION/LOCATION_SHARING/[URL-encoded Family Member Name]'&gt;prefs:root=Privacy&amp;amp;path=LOCATION/LOCATION_SHARING/[URL-encoded Family Member Name]&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;AppName&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;path=LOCATION/[App Bundle ID]'&gt;prefs:root=Privacy&amp;amp;path=LOCATION/[App Bundle ID]&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;System Services&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;path=LOCATION/SYSTEM_SERVICES'&gt;prefs:root=Privacy&amp;amp;path=LOCATION/SYSTEM_SERVICES&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Tracking&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;path=USER_TRACKING'&gt;prefs:root=Privacy&amp;amp;path=USER_TRACKING&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Contacts&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;path=CONTACTS'&gt;prefs:root=Privacy&amp;amp;path=CONTACTS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Calendars&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;path=CALENDARS'&gt;prefs:root=Privacy&amp;amp;path=CALENDARS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Reminders&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;path=REMINDERS'&gt;prefs:root=Privacy&amp;amp;path=REMINDERS&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Photos&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;path=PHOTOS'&gt;prefs:root=Privacy&amp;amp;path=PHOTOS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;AppName&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;path=PHOTOS/[App Bundle ID]'&gt;prefs:root=Privacy&amp;amp;path=PHOTOS/[App Bundle ID]&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Bluetooth&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;path=BT_PERIPHERAL'&gt;prefs:root=Privacy&amp;amp;path=BT_PERIPHERAL&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Local Network&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;path=LOCAL_NETWORK'&gt;prefs:root=Privacy&amp;amp;path=LOCAL_NETWORK&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Microphone&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;path=MICROPHONE'&gt;prefs:root=Privacy&amp;amp;path=MICROPHONE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Speech Recognition&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;path=SPEECH_RECOGNITION'&gt;prefs:root=Privacy&amp;amp;path=SPEECH_RECOGNITION&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Camera&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;path=CAMERA'&gt;prefs:root=Privacy&amp;amp;path=CAMERA&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Health&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;path=HEALTH'&gt;prefs:root=Privacy&amp;amp;path=HEALTH&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;HomeKit&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;path=WILLOW'&gt;prefs:root=Privacy&amp;amp;path=WILLOW&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Media &amp;amp; Apple Music&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;path=MEDIALIBRARY'&gt;prefs:root=Privacy&amp;amp;path=MEDIALIBRARY&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Motion &amp;amp; Fitness&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;path=MOTION'&gt;prefs:root=Privacy&amp;amp;path=MOTION&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Analytics &amp;amp; Improvements&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;path=PROBLEM_REPORTING'&gt;prefs:root=Privacy&amp;amp;path=PROBLEM_REPORTING&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Advertising&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;path=ADVERTISING'&gt;prefs:root=Privacy&amp;amp;path=ADVERTISING&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Record App Activity&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Privacy&amp;path=PRIVACY_REPORT'&gt;prefs:root=Privacy&amp;amp;path=PRIVACY_REPORT&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;App Store&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=STORE'&gt;prefs:root=STORE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;App Downloads&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=STORE&amp;path=App%20Downloads'&gt;prefs:root=STORE&amp;amp;path=App%20Downloads&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Video Autoplay&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=STORE&amp;path=Video%20Autoplay'&gt;prefs:root=STORE&amp;amp;path=Video%20Autoplay&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Wallet&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=PASSBOOK'&gt;prefs:root=PASSBOOK&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Add Card&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=PASSBOOK&amp;path=Add%20Card'&gt;prefs:root=PASSBOOK&amp;amp;path=Add%20Card&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Passwords&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=PASSWORDS'&gt;prefs:root=PASSWORDS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;[Search Term]&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=PASSWORDS&amp;search=[Search Term]'&gt;prefs:root=PASSWORDS&amp;amp;search=[Search Term]&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Passwords &amp;amp; Accounts&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCOUNTS_AND_PASSWORDS'&gt;prefs:root=ACCOUNTS_AND_PASSWORDS&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Add Account&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCOUNTS_AND_PASSWORDS&amp;path=ADD_ACCOUNT'&gt;prefs:root=ACCOUNTS_AND_PASSWORDS&amp;amp;path=ADD_ACCOUNT&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;iCloud&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCOUNTS_AND_PASSWORDS&amp;path=ADD_ACCOUNT/iCloud'&gt;prefs:root=ACCOUNTS_AND_PASSWORDS&amp;amp;path=ADD_ACCOUNT/iCloud&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Google&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCOUNTS_AND_PASSWORDS&amp;path=ADD_ACCOUNT/Gmail'&gt;prefs:root=ACCOUNTS_AND_PASSWORDS&amp;amp;path=ADD_ACCOUNT/Gmail&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;AOL&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCOUNTS_AND_PASSWORDS&amp;path=ADD_ACCOUNT/AOL'&gt;prefs:root=ACCOUNTS_AND_PASSWORDS&amp;amp;path=ADD_ACCOUNT/AOL&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Outlook&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCOUNTS_AND_PASSWORDS&amp;path=ADD_ACCOUNT/Outlook'&gt;prefs:root=ACCOUNTS_AND_PASSWORDS&amp;amp;path=ADD_ACCOUNT/Outlook&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Other&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCOUNTS_AND_PASSWORDS&amp;path=ADD_ACCOUNT/OTHER'&gt;prefs:root=ACCOUNTS_AND_PASSWORDS&amp;amp;path=ADD_ACCOUNT/OTHER&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Add Mail Account&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCOUNTS_AND_PASSWORDS&amp;path=ADD_ACCOUNT/OTHER/Add%20Mail%20Account'&gt;prefs:root=ACCOUNTS_AND_PASSWORDS&amp;amp;path=ADD_ACCOUNT/OTHER/Add%20Mail%20Account&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Add LDAP Account&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCOUNTS_AND_PASSWORDS&amp;path=ADD_ACCOUNT/OTHER/Add%20LDAP%20Account'&gt;prefs:root=ACCOUNTS_AND_PASSWORDS&amp;amp;path=ADD_ACCOUNT/OTHER/Add%20LDAP%20Account&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Add CardDAV Account&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCOUNTS_AND_PASSWORDS&amp;path=ADD_ACCOUNT/OTHER/Add%20CardDAV%20Account'&gt;prefs:root=ACCOUNTS_AND_PASSWORDS&amp;amp;path=ADD_ACCOUNT/OTHER/Add%20CardDAV%20Account&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Add CalDAV Account&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCOUNTS_AND_PASSWORDS&amp;path=ADD_ACCOUNT/OTHER/Add%20CalDAV%20Account'&gt;prefs:root=ACCOUNTS_AND_PASSWORDS&amp;amp;path=ADD_ACCOUNT/OTHER/Add%20CalDAV%20Account&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Add Subscribed Calendar&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCOUNTS_AND_PASSWORDS&amp;path=ADD_ACCOUNT/OTHER/Add%20Subscribed%20Calendar'&gt;prefs:root=ACCOUNTS_AND_PASSWORDS&amp;amp;path=ADD_ACCOUNT/OTHER/Add%20Subscribed%20Calendar&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Fetch New Data&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=ACCOUNTS_AND_PASSWORDS&amp;path=FETCH_NEW_DATA'&gt;prefs:root=ACCOUNTS_AND_PASSWORDS&amp;amp;path=FETCH_NEW_DATA&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Mail&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL'&gt;prefs:root=MAIL&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Notifications&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL&amp;path=NOTIFICATIONS'&gt;prefs:root=MAIL&amp;amp;path=NOTIFICATIONS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Preview&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL&amp;path=Preview'&gt;prefs:root=MAIL&amp;amp;path=Preview&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Swipe Options&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL&amp;path=Swipe%20Options'&gt;prefs:root=MAIL&amp;amp;path=Swipe%20Options&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Swipe Left&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL&amp;path=Swipe%20Options/Swipe%20Left'&gt;prefs:root=MAIL&amp;amp;path=Swipe%20Options/Swipe%20Left&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Swipe Right&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL&amp;path=Swipe%20Options/Swipe%20Right'&gt;prefs:root=MAIL&amp;amp;path=Swipe%20Options/Swipe%20Right&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Blocked&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL&amp;path=Blocked'&gt;prefs:root=MAIL&amp;amp;path=Blocked&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Muted Thread Action&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL&amp;path=Muted%20Thread%20Action'&gt;prefs:root=MAIL&amp;amp;path=Muted%20Thread%20Action&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Blocked Sender Options&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL&amp;path=Blocked%20Sender%20Options'&gt;prefs:root=MAIL&amp;amp;path=Blocked%20Sender%20Options&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Mark Addresses&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL&amp;path=Mark%20Addresses'&gt;prefs:root=MAIL&amp;amp;path=Mark%20Addresses&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Increase Quote Level&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL&amp;path=Increase%20Quote%20Level'&gt;prefs:root=MAIL&amp;amp;path=Increase%20Quote%20Level&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Include Attachments with Replies&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL&amp;path=Include%20Attachments%20with%20Replies'&gt;prefs:root=MAIL&amp;amp;path=Include%20Attachments%20with%20Replies&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Signature&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL&amp;path=Signature'&gt;prefs:root=MAIL&amp;amp;path=Signature&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Default Account&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAIL&amp;path=Default%20Account'&gt;prefs:root=MAIL&amp;amp;path=Default%20Account&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Contacts&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CONTACTS'&gt;prefs:root=CONTACTS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Siri &amp;amp; Search&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CONTACTS&amp;path=SIRI_AND_SEARCH'&gt;prefs:root=CONTACTS&amp;amp;path=SIRI_AND_SEARCH&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Sort Order&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CONTACTS&amp;path=ContactsSortOrder'&gt;prefs:root=CONTACTS&amp;amp;path=ContactsSortOrder&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Display Order&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CONTACTS&amp;path=PersonNameOrder'&gt;prefs:root=CONTACTS&amp;amp;path=PersonNameOrder&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Short Name&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CONTACTS&amp;path=PersonShortName'&gt;prefs:root=CONTACTS&amp;amp;path=PersonShortName&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;My Info&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CONTACTS&amp;path=MeCard'&gt;prefs:root=CONTACTS&amp;amp;path=MeCard&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Calendar&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CALENDAR'&gt;prefs:root=CALENDAR&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Siri &amp;amp; Search&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CALENDAR&amp;path=SIRI_AND_SEARCH'&gt;prefs:root=CALENDAR&amp;amp;path=SIRI_AND_SEARCH&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Time Zone Override&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CALENDAR&amp;path=TimeZoneCityArray'&gt;prefs:root=CALENDAR&amp;amp;path=TimeZoneCityArray&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Alternate Calendars&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CALENDAR&amp;path=Alternate%20Calendars'&gt;prefs:root=CALENDAR&amp;amp;path=Alternate%20Calendars&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Sync&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CALENDAR&amp;path=Sync'&gt;prefs:root=CALENDAR&amp;amp;path=Sync&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Default Alert Times&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CALENDAR&amp;path=Default%20Alert%20Times'&gt;prefs:root=CALENDAR&amp;amp;path=Default%20Alert%20Times&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Start Week On&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CALENDAR&amp;path=com.apple.mobilecal'&gt;prefs:root=CALENDAR&amp;amp;path=com.apple.mobilecal&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Default Calendar&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CALENDAR&amp;path=Default%20Calendar'&gt;prefs:root=CALENDAR&amp;amp;path=Default%20Calendar&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Notes&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=NOTES'&gt;prefs:root=NOTES&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Siri &amp;amp; Search&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=NOTES&amp;path=SIRI_AND_SEARCH'&gt;prefs:root=NOTES&amp;amp;path=SIRI_AND_SEARCH&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Default Account&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=NOTES&amp;path=Default%20Account'&gt;prefs:root=NOTES&amp;amp;path=Default%20Account&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Password&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=NOTES&amp;path=Password'&gt;prefs:root=NOTES&amp;amp;path=Password&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Sort Notes By&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=NOTES&amp;path=Sort%20Notes%20By'&gt;prefs:root=NOTES&amp;amp;path=Sort%20Notes%20By&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;New Notes Start With&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=NOTES&amp;path=New%20Notes%20Start%20With'&gt;prefs:root=NOTES&amp;amp;path=New%20Notes%20Start%20With&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Sort Checked Items&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=NOTES&amp;path=Sort%20Checked%20Items'&gt;prefs:root=NOTES&amp;amp;path=Sort%20Checked%20Items&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Lines &amp;amp; Grids&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=NOTES&amp;path=Lines%20%26%20Grids'&gt;prefs:root=NOTES&amp;amp;path=Lines%20%26%20Grids&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Note Backgrounds&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=NOTES&amp;path=Note%20Backgrounds'&gt;prefs:root=NOTES&amp;amp;path=Note%20Backgrounds&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Access Notes from Lock Screen&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=NOTES&amp;path=Access%20Notes%20from%20Lock%20Screen'&gt;prefs:root=NOTES&amp;amp;path=Access%20Notes%20from%20Lock%20Screen&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Reminders&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=REMINDERS'&gt;prefs:root=REMINDERS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Siri &amp;amp; Search&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=REMINDERS&amp;path=SIRI_AND_SEARCH'&gt;prefs:root=REMINDERS&amp;amp;path=SIRI_AND_SEARCH&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Default List&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=REMINDERS&amp;path=DEFAULT_LIST'&gt;prefs:root=REMINDERS&amp;amp;path=DEFAULT_LIST&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Voice Memos&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=VOICE_MEMOS'&gt;prefs:root=VOICE_MEMOS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Siri &amp;amp; Search&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=VOICE_MEMOS&amp;path=SIRI_AND_SEARCH'&gt;prefs:root=VOICE_MEMOS&amp;amp;path=SIRI_AND_SEARCH&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Clear Deleted&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=VOICE_MEMOS&amp;path=RCVoiceMemosRecentlyDeletedWindowKey'&gt;prefs:root=VOICE_MEMOS&amp;amp;path=RCVoiceMemosRecentlyDeletedWindowKey&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Audio Quality&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=VOICE_MEMOS&amp;path=RCVoiceMemosAudioQualityKey'&gt;prefs:root=VOICE_MEMOS&amp;amp;path=RCVoiceMemosAudioQualityKey&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Phone&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Phone'&gt;prefs:root=Phone&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Respond with Text&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Phone&amp;path=Respond%20with%20Text'&gt;prefs:root=Phone&amp;amp;path=Respond%20with%20Text&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Silence Unknown Callers&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Phone&amp;path=SILENCE_CALLS'&gt;prefs:root=Phone&amp;amp;path=SILENCE_CALLS&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Messages&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MESSAGES'&gt;prefs:root=MESSAGES&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;FaceTime&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=FACETIME'&gt;prefs:root=FACETIME&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Siri &amp;amp; Search&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=FACETIME&amp;path=SIRI_AND_SEARCH'&gt;prefs:root=FACETIME&amp;amp;path=SIRI_AND_SEARCH&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Safari&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SAFARI'&gt;prefs:root=SAFARI&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;AutoFill&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SAFARI&amp;path=AUTO_FILL'&gt;prefs:root=SAFARI&amp;amp;path=AUTO_FILL&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Content Blockers&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SAFARI&amp;path=CONTENT_BLOCKERS'&gt;prefs:root=SAFARI&amp;amp;path=CONTENT_BLOCKERS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Downloads&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SAFARI&amp;path=DOWNLOADS'&gt;prefs:root=SAFARI&amp;amp;path=DOWNLOADS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Close Tabs&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SAFARI&amp;path=Close%20Tabs'&gt;prefs:root=SAFARI&amp;amp;path=Close%20Tabs&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Hide IP Address&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SAFARI&amp;path=Hide%20IP%20Address'&gt;prefs:root=SAFARI&amp;amp;path=Hide%20IP%20Address&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Clear History and Website Data&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SAFARI&amp;path=CLEAR_HISTORY_AND_DATA'&gt;prefs:root=SAFARI&amp;amp;path=CLEAR_HISTORY_AND_DATA&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Page Zoom&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SAFARI&amp;path=Page%20Zoom'&gt;prefs:root=SAFARI&amp;amp;path=Page%20Zoom&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Request Desktop Website&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SAFARI&amp;path=Request%20Desktop%20Website'&gt;prefs:root=SAFARI&amp;amp;path=Request%20Desktop%20Website&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Reader&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SAFARI&amp;path=Reader'&gt;prefs:root=SAFARI&amp;amp;path=Reader&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Camera&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SAFARI&amp;path=Camera'&gt;prefs:root=SAFARI&amp;amp;path=Camera&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Microphone&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SAFARI&amp;path=Microphone'&gt;prefs:root=SAFARI&amp;amp;path=Microphone&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Location&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SAFARI&amp;path=Location'&gt;prefs:root=SAFARI&amp;amp;path=Location&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Advanced&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SAFARI&amp;path=ADVANCED'&gt;prefs:root=SAFARI&amp;amp;path=ADVANCED&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;News&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=NEWS'&gt;prefs:root=NEWS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Acknowledgements&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=NEWS&amp;path=Acknowledgements'&gt;prefs:root=NEWS&amp;amp;path=Acknowledgements&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Translate&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=TRANSLATE'&gt;prefs:root=TRANSLATE&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Maps&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAPS'&gt;prefs:root=MAPS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Driving&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAPS&amp;path=Driving'&gt;prefs:root=MAPS&amp;amp;path=Driving&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Walking&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAPS&amp;path=Walking'&gt;prefs:root=MAPS&amp;amp;path=Walking&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Transit&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAPS&amp;path=Transit'&gt;prefs:root=MAPS&amp;amp;path=Transit&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Cycling&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MAPS&amp;path=Cycling'&gt;prefs:root=MAPS&amp;amp;path=Cycling&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Compass&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=COMPASS'&gt;prefs:root=COMPASS&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Measure&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MEASURE'&gt;prefs:root=MEASURE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Siri &amp;amp; Search&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MEASURE&amp;path=SIRI_AND_SEARCH'&gt;prefs:root=MEASURE&amp;amp;path=SIRI_AND_SEARCH&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Shortcuts&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SHORTCUTS'&gt;prefs:root=SHORTCUTS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Legal Notices&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=SHORTCUTS&amp;path=Legal%20Notices'&gt;prefs:root=SHORTCUTS&amp;amp;path=Legal%20Notices&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Health&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=HEALTH'&gt;prefs:root=HEALTH&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Siri &amp;amp; Search&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=HEALTH&amp;path=SIRI_AND_SEARCH'&gt;prefs:root=HEALTH&amp;amp;path=SIRI_AND_SEARCH&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Home&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=HOMEKIT'&gt;prefs:root=HOMEKIT&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Music&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MUSIC'&gt;prefs:root=MUSIC&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Cellular Data&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MUSIC&amp;path=com.apple.Music%3ACellularData'&gt;prefs:root=MUSIC&amp;amp;path=com.apple.Music%3ACellularData&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Dolby Atmos&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MUSIC&amp;path=com.apple.Music%3AAtmos'&gt;prefs:root=MUSIC&amp;amp;path=com.apple.Music%3AAtmos&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Audio Quality&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MUSIC&amp;path=com.apple.Music%3AAudioQuality'&gt;prefs:root=MUSIC&amp;amp;path=com.apple.Music%3AAudioQuality&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;EQ&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MUSIC&amp;path=com.apple.Music%3AEQ'&gt;prefs:root=MUSIC&amp;amp;path=com.apple.Music%3AEQ&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Optimize Storage&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MUSIC&amp;path=com.apple.Music%3AOptimizeStorage'&gt;prefs:root=MUSIC&amp;amp;path=com.apple.Music%3AOptimizeStorage&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Volume Limit&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=MUSIC&amp;path=com.apple.Music%3AVolumeLimit'&gt;prefs:root=MUSIC&amp;amp;path=com.apple.Music%3AVolumeLimit&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;TV&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=TVAPP'&gt;prefs:root=TVAPP&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Cellular&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=TVAPP&amp;path=com.apple.videos%3AVideosPlaybackQualityCellularSetting'&gt;prefs:root=TVAPP&amp;amp;path=com.apple.videos%3AVideosPlaybackQualityCellularSetting&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Wi-Fi&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=TVAPP&amp;path=com.apple.videos%3AVideosPlaybackQualitySetting'&gt;prefs:root=TVAPP&amp;amp;path=com.apple.videos%3AVideosPlaybackQualitySetting&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Siri &amp;amp; Search&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=TVAPP&amp;path=SIRI_AND_SEARCH'&gt;prefs:root=TVAPP&amp;amp;path=SIRI_AND_SEARCH&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Video Definition&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=TVAPP&amp;path=com.apple.videos%3APreferredPurchaseResolution'&gt;prefs:root=TVAPP&amp;amp;path=com.apple.videos%3APreferredPurchaseResolution&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Photos&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Photos'&gt;prefs:root=Photos&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Cellular Data&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=Photos&amp;path=CellularDataLinkList'&gt;prefs:root=Photos&amp;amp;path=CellularDataLinkList&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Camera&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CAMERA'&gt;prefs:root=CAMERA&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Formats&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CAMERA&amp;path=CameraFormatsSettingsList'&gt;prefs:root=CAMERA&amp;amp;path=CameraFormatsSettingsList&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Record Video&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CAMERA&amp;path=Record%20Video'&gt;prefs:root=CAMERA&amp;amp;path=Record%20Video&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Record Slo-mo&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CAMERA&amp;path=Record%20Slo-mo'&gt;prefs:root=CAMERA&amp;amp;path=Record%20Slo-mo&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Preserve Settings&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=CAMERA&amp;path=CameraPreserveSettingsSwitch'&gt;prefs:root=CAMERA&amp;amp;path=CameraPreserveSettingsSwitch&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Books&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=IBOOKS'&gt;prefs:root=IBOOKS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Siri &amp;amp; Search&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=IBOOKS&amp;path=SIRI_AND_SEARCH'&gt;prefs:root=IBOOKS&amp;amp;path=SIRI_AND_SEARCH&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Skip Forward&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=IBOOKS&amp;path=BKAudioBookSkipForward'&gt;prefs:root=IBOOKS&amp;amp;path=BKAudioBookSkipForward&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Skip Back&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=IBOOKS&amp;path=BKAudioBookSkipBackward'&gt;prefs:root=IBOOKS&amp;amp;path=BKAudioBookSkipBackward&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Acknowledgements&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=IBOOKS&amp;path=Acknowledgements'&gt;prefs:root=IBOOKS&amp;amp;path=Acknowledgements&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Podcasts&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=com.apple.podcasts'&gt;prefs:root=com.apple.podcasts&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Game Center&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=GAMECENTER'&gt;prefs:root=GAMECENTER&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Add Friends&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=GAMECENTER&amp;path=Add%20Friends'&gt;prefs:root=GAMECENTER&amp;amp;path=Add%20Friends&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Terms and Conditions&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=GAMECENTER&amp;path=Terms%20and%20Conditions'&gt;prefs:root=GAMECENTER&amp;amp;path=Terms%20and%20Conditions&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;TV Provider&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=VIDEO_SUBSCRIBER'&gt;prefs:root=VIDEO_SUBSCRIBER&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Developer&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS'&gt;prefs:root=DEVELOPER_SETTINGS&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Instruments/Logging&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS&amp;path=DTInstrumentsSettings'&gt;prefs:root=DEVELOPER_SETTINGS&amp;amp;path=DTInstrumentsSettings&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Network Link Conditioner&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS&amp;path=NLC'&gt;prefs:root=DEVELOPER_SETTINGS&amp;amp;path=NLC&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Multipath Networking&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS&amp;path=MULTI_PATH_AGG'&gt;prefs:root=DEVELOPER_SETTINGS&amp;amp;path=MULTI_PATH_AGG&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Fill Rate&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS&amp;path=FILL_RATE'&gt;prefs:root=DEVELOPER_SETTINGS&amp;amp;path=FILL_RATE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Ad Refresh Rate&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS&amp;path=AD_REFRESH_RATE'&gt;prefs:root=DEVELOPER_SETTINGS&amp;amp;path=AD_REFRESH_RATE&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;TV Provider&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=DEVELOPER_SETTINGS&amp;path=VideoSubscriberAccountSettings'&gt;prefs:root=DEVELOPER_SETTINGS&amp;amp;path=VideoSubscriberAccountSettings&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Settings For App…&lt;/h1&gt;
&lt;h3&gt;root&lt;/h3&gt;
&lt;p&gt;&lt;a href='prefs:root=[App Bundle ID]'&gt;prefs:root=[App Bundle ID]&lt;/a&gt;&lt;/p&gt;
&lt;/details&gt;
&lt;h2&gt;How to get the latest values&lt;/h2&gt;
&lt;p&gt;&lt;a name='getTheLatestValues'&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Get the latest values:&lt;/h2&gt;
&lt;p&gt;The steps for fetching the new values.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open Xcode&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Download iOS Simulator&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Xcode -&amp;gt; Settings (&lt;code&gt;⌘,&lt;/code&gt;) -&amp;gt; Components&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Right click on the Simulator, click on &lt;code&gt;show in Finder&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Right click on iOS xxxx.simruntime, click on &lt;code&gt;Show package contents&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Navigate to this folder in Terminal, I always like to use this particular command: &lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;osascript -e 'tell application "Finder"'\
     -e "if (${1-1} &amp;lt;= (count Finder windows)) then"\
     -e "get POSIX path of (target of window ${1-1} as alias)"\
     -e 'else' -e 'get POSIX path of (desktop as alias)'\
     -e 'end if' -e 'end tell';&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Type: &lt;code&gt;open -R Contents/Resources/RuntimeRoot/System/Library/PreferenceManifestsInternal/PreferencesManifests.bundle/&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the plist files you'll find the values; example:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-xml"&gt;&amp;lt;dict&amp;gt;
 &amp;lt;key&amp;gt;keywords&amp;lt;/key&amp;gt;
 &amp;lt;string&amp;gt;PASSCODE_KEYWORDS&amp;lt;/string&amp;gt;
 &amp;lt;key&amp;gt;label&amp;lt;/key&amp;gt;
 &amp;lt;string&amp;gt;TOUCH_ID__PASSCODE&amp;lt;/string&amp;gt;
 &amp;lt;key&amp;gt;requiredCapabilities&amp;lt;/key&amp;gt;
 &amp;lt;array&amp;gt;
     &amp;lt;string&amp;gt;touch-id&amp;lt;/string&amp;gt;
 &amp;lt;/array&amp;gt;
 &amp;lt;key&amp;gt;searchSectionID&amp;lt;/key&amp;gt;
 &amp;lt;string&amp;gt;PASSCODE&amp;lt;/string&amp;gt;
 &amp;lt;key&amp;gt;searchURL&amp;lt;/key&amp;gt;
 &amp;lt;string&amp;gt;prefs:root=PASSCODE&amp;lt;/string&amp;gt;
&amp;lt;/dict&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;(Semi-Automatically get the new values)&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-php"&gt;&amp;lt;?php
// Extract all iOS Settings URLs with their localized labels
// Please note, this script assumes that the files are in XML format.
// To convert binary plist files to XML, you can use the command:
// plutil -convert xml1 *

// Find all *.plist files in this directory
$files = glob("en.lproj/*.strings");

// This is our strings table to get the 'english' labels
$stringsTable = array();

foreach ($files as $file) {
    $fileContents = file_get_contents($file);

    // Find all '&amp;lt;key&amp;gt;...&amp;lt;/key&amp;gt;' and corresponding '&amp;lt;string&amp;gt;...&amp;lt;/string&amp;gt;' pairs
    preg_match_all('/&amp;lt;key&amp;gt;(.*?)&amp;lt;\/key&amp;gt;\s*&amp;lt;string&amp;gt;(.*?)&amp;lt;\/string&amp;gt;/', $fileContents, $matches, PREG_SET_ORDER);

    foreach ($matches as $match) {
        // Ensure both key and value are set
        if (!isset($match[1]) || !isset($match[2])) {
            continue;
        }

        $key = $match[1];
        $value = $match[2];
        $stringsTable[$key] = $value;
    }
}

// Find all *.plist files in this directory
$files = glob("*.plist");

$markdown = "# iOS Settings URLs\n\n";
$markdown .= "Latest update: " . date("Y-m-d H:i:s") . ".\n\n";

foreach ($files as $file) {
    if (preg_match('/Info.plist/', $file)) {
        continue;
    }

    $fileContents = file_get_contents($file);
    // Find all '&amp;lt;key&amp;gt;label&amp;lt;/key&amp;gt;' and corresponding '&amp;lt;string&amp;gt;' pairs
    preg_match_all(
        '/&amp;lt;key&amp;gt;label&amp;lt;\/key&amp;gt;\s*&amp;lt;string&amp;gt;(.*?)&amp;lt;\/string&amp;gt;/',
        $fileContents, $matches1, PREG_SET_ORDER
    );

    // Find all '&amp;lt;key&amp;gt;searchURL&amp;lt;/key&amp;gt;' and corresponding '&amp;lt;string&amp;gt;' pairs
    preg_match_all(
        '/&amp;lt;key&amp;gt;searchURL&amp;lt;\/key&amp;gt;\s*&amp;lt;string&amp;gt;(.*?)&amp;lt;\/string&amp;gt;/',
        $fileContents, $matches2, PREG_SET_ORDER
    );

    if (sizeof($matches1) !== sizeof($matches2)) {
        echo "Mismatched pairs in file: $file\n";
        continue;
    }

    for ($i = 0; $i &amp;lt; sizeof($matches1); $i++) {
        $key = $matches1[$i][1];
        $value = $matches2[$i][1];
        $label = $stringsTable[$key] ?? $key;
        $markdown .= "### {$label}\n({$value})[{$value}]\n\n";
    }
}

echo $markdown;&lt;/code&gt;&lt;/pre&gt;</description>
  </item>
  <item>
    <title>Why You Should Avoid Using AnyView in SwiftUI</title>    <link>https://wesleydegroot.nl/blog/why-you-should-avoid-anyview-in-swiftui</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/why-you-should-avoid-anyview-in-swiftui</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:57 +0200</pubDate>
    <category>SwiftUI</category>
    <category>AnyView</category>
    <description>&lt;p&gt;SwiftUI, Apple's declarative framework for building user interfaces, introduces a variety of new concepts and tools for developers.&lt;br&gt;
One such tool is &lt;code&gt;AnyView&lt;/code&gt;, a type-erased wrapper for views.&lt;br&gt;
While &lt;code&gt;AnyView&lt;/code&gt; can be useful in certain scenarios, it’s generally advisable to avoid using it unless absolutely necessary.&lt;/p&gt;
&lt;p&gt;Here’s why:&lt;/p&gt;
&lt;h3&gt;1. &lt;strong&gt;Performance Overhead&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;AnyView&lt;/code&gt; introduces a performance overhead because it erases the type information of the wrapped view.&lt;br&gt;
This type erasure means that SwiftUI has to perform additional work to manage the view hierarchy, which can lead to slower rendering times and increased memory usage.&lt;br&gt;
In performance-critical applications, this overhead can become significant.&lt;/p&gt;
&lt;h3&gt;2. &lt;strong&gt;Loss of Type Safety&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;One of Swift’s greatest strengths is its strong type system, which helps catch errors at compile time.&lt;br&gt;
By using &lt;code&gt;AnyView&lt;/code&gt;, you lose the benefits of type safety, making your code more prone to runtime errors.&lt;br&gt;
This can lead to harder-to-debug issues and a less reliable codebase.&lt;/p&gt;
&lt;h3&gt;3. &lt;strong&gt;Reduced Readability and Maintainability&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Using &lt;code&gt;AnyView&lt;/code&gt; can make your code less readable and harder to maintain.&lt;br&gt;
When you wrap a view in &lt;code&gt;AnyView&lt;/code&gt;, you obscure the actual type of the view, making it more difficult for other developers (or even yourself) to understand what’s going on.&lt;br&gt;
Clear and explicit code is easier to read, understand, and maintain.&lt;/p&gt;
&lt;h3&gt;4. &lt;strong&gt;Limited View Modifiers&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;When you use &lt;code&gt;AnyView&lt;/code&gt;, you lose the ability to apply view-specific modifiers.&lt;br&gt;
For example, if you wrap a &lt;code&gt;Text&lt;/code&gt; view in &lt;code&gt;AnyView&lt;/code&gt;, you can no longer use &lt;code&gt;Text&lt;/code&gt;-specific modifiers like &lt;code&gt;.font()&lt;/code&gt; or &lt;code&gt;.foregroundColor()&lt;/code&gt;.&lt;br&gt;
This limitation can force you to unwrap the view or use less efficient workarounds.&lt;/p&gt;
&lt;h3&gt;5. &lt;strong&gt;Complicates View Hierarchy&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;AnyView&lt;/code&gt; can complicate your view hierarchy, making it harder to reason about the structure and behavior of your UI.&lt;br&gt;
SwiftUI relies on the view hierarchy to manage state and rendering efficiently.&lt;br&gt;
Introducing &lt;code&gt;AnyView&lt;/code&gt; can disrupt this hierarchy, leading to unexpected behavior and bugs.&lt;/p&gt;
&lt;h3&gt;Alternatives to &lt;code&gt;AnyView&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Instead of using &lt;code&gt;AnyView&lt;/code&gt;, consider these alternatives:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Conditional Views&lt;/strong&gt;: Use conditional statements to handle different view types directly. SwiftUI’s &lt;code&gt;Group&lt;/code&gt; and &lt;a href="https://wesleydegroot.nl/blog/@ViewBuilder"&gt;&lt;code&gt;@ViewBuilder&lt;/code&gt;&lt;/a&gt; can help manage complex view hierarchies without type erasure.&lt;br&gt;
See also my blog post about &lt;a href="https://wesleydegroot.nl/blog/@ViewBuilder"&gt;&lt;code&gt;@ViewBuilder&lt;/code&gt;&lt;/a&gt;'s for more information.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;@ViewBuilder
var body: some View {
    if condition {
        Text("Hello, World!")
    } else {
        Image(systemName: "star")
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Enum-Based Views&lt;/strong&gt;: Use enums to represent different view states and switch between them using &lt;code&gt;switch&lt;/code&gt; statements. This approach maintains type safety and clarity.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;enum ContentView {
    case text
    case image
}

var body: some View {
    switch contentView {
    case .text:
        Text("Hello, World!")
    case .image:
        Image(systemName: "star")
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;While &lt;code&gt;AnyView&lt;/code&gt; can be a quick fix for certain problems, it’s generally best to avoid it due to the performance overhead, loss of type safety, reduced readability, limited view modifiers, and complications in the view hierarchy.&lt;br&gt;
By using alternative approaches, you can maintain the benefits of Swift’s type system and create more efficient, maintainable, and reliable SwiftUI applications.&lt;/p&gt;</description>
  </item>
  <item>
    <title>A Guide to UI Testing in Swift</title>    <link>https://wesleydegroot.nl/blog/ui-testing-in-swift</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/ui-testing-in-swift</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:58 +0200</pubDate>
    <category>Swift</category>
    <category>UITest</category>
    <category>XCUITest</category>
    <description>&lt;p&gt;User Interface (UI) testing is a crucial aspect of app development, ensuring that your app's interface behaves as expected under various conditions.&lt;br&gt;
This guide will walk you through the basics of UI testing in Swift and provide tips for creating effective tests.&lt;/p&gt;
&lt;h2&gt;What is UI Testing?&lt;/h2&gt;
&lt;p&gt;UI testing involves interacting with your app's user interface to verify that it functions correctly.&lt;br&gt;
This includes checking that buttons respond to taps, text fields accept input, and views display the correct content.&lt;br&gt;
UI tests simulate user interactions and can help catch bugs that might not be evident through unit tests alone.&lt;/p&gt;
&lt;h2&gt;Why UI Testing is Important&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Catch UI Bugs Early&lt;/strong&gt;: UI tests can detect issues such as layout problems, missing elements, or incorrect text before users encounter them.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Improve User Experience&lt;/strong&gt;: By ensuring that your app's interface works as intended, you can provide a better experience for your users.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Increase Confidence in Code Changes&lt;/strong&gt;: UI tests can help you verify that new features or changes don't break existing functionality.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Automate Testing&lt;/strong&gt;: UI tests can be automated to run on different devices and configurations, saving time and effort during the testing process.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Key Components of UI Testing&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://developer.apple.com/documentation/xctest/XCUIApplication"&gt;XCUIApplication&lt;/a&gt;&lt;/strong&gt;: Represents your app and provides methods to launch, terminate, and interact with it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://developer.apple.com/documentation/xctest/XCUIElement"&gt;XCUIElement&lt;/a&gt;&lt;/strong&gt;: Represents a UI element in your app, such as a button, label, or text field. You can query and interact with these elements.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://developer.apple.com/documentation/xctest/xcuielementquery"&gt;XCUIElementQuery&lt;/a&gt;&lt;/strong&gt;: An object that defines the search criteria a test uses to identify UI elements.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://developer.apple.com/documentation/xctest/XCTAssert"&gt;XCTAssert&lt;/a&gt;&lt;/strong&gt;: Provides various assertion methods to verify that your tests produce the expected results.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Writing Effective UI Tests&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Use Accessibility Identifiers&lt;/strong&gt;: Assign unique accessibility identifiers to your UI elements.&lt;br&gt;
This makes it easier to locate and interact with elements in your tests.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;button.accessibilityIdentifier = "MyButton"&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Launch Arguments and Environment Variables&lt;/strong&gt;: Use launch arguments and environment variables to configure your app's state before running tests.&lt;br&gt;
This can help you test different scenarios without modifying your app's code.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;let app = XCUIApplication()
app.launchArguments.append("--UITestMode")
app.launchEnvironment["username"] = "testUser"
app.launch()&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Wait for Elements&lt;/strong&gt;: Use expectations to wait for elements to appear or disappear.&lt;br&gt;
This ensures that your tests don't fail due to timing issues.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;let label = app.staticTexts["MyLabel"]
let exists = NSPredicate(format: "exists == true")
expectation(for: exists, evaluatedWith: label, handler: nil)
waitForExpectations(timeout: 5, handler: nil)&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Test Different Devices and Orientations&lt;/strong&gt;: Run your tests on different devices and orientations to ensure your app works well across various screen sizes and configurations.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Running and Debugging UI Tests&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Run Tests&lt;/strong&gt;: You can run your UI tests by selecting the test target and clicking the "Run" button in Xcode.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Debug Tests&lt;/strong&gt;: If a test fails, Xcode provides detailed logs and screenshots to help you diagnose the issue. Use breakpoints and the debugger to step through your test code and inspect the app's state.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;UI Test functions&lt;/h2&gt;
&lt;h3&gt;Basic Functionality&lt;/h3&gt;
&lt;h4&gt;Testing if an element exists&lt;/h4&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;XCTAssert(app.staticTexts["Welcome"].exists)&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Waiting for an element to appear&lt;/h4&gt;
&lt;p&gt;"Waiting" is now built into XCTest.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;let throwPokeball = app.staticTexts["The pokemon escaped!"]
XCTAssertFalse(throwPokeball.exists)

app.buttons["Catch!"].tap()
XCTAssert(throwPokeball.waitForExistence(timeout: 5))&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Interacting with System Controls&lt;/h3&gt;
&lt;h4&gt;Tapping buttons&lt;/h4&gt;
&lt;p&gt;Identify buttons by their accessibility label.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;app.buttons["Add"].tap()&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Typing text&lt;/h4&gt;
&lt;p&gt;First make sure the text field has focus by tapping on it.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;let textField = app.textFields["Username"]
textField.tap()
textField.typeText("Wesley de Groot")&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Dismissing alerts&lt;/h4&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;app.alerts["Alert Title"].buttons["Button Title"].tap()&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Dismissing action sheets&lt;/h4&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;app.sheets["Sheet Title"].buttons["Button Title"].tap()&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Handling system alerts&lt;/h4&gt;
&lt;p&gt;Present a location services authorization dialog to the user and dismiss it with the following code.&lt;/p&gt;
&lt;p&gt;Before presenting the alert add a UI Interruption Handler. When this fires, dismiss with the "Allow" button.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;addUIInterruptionMonitor(withDescription: "Location Services") { (alert) -&amp;gt; Bool in
  alert.buttons["Allow"].tap()
  return true
}

app.buttons["Request Location"].tap()
app.tap() // need to interact with the app again for the handler to fire&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Sliding sliders&lt;/h4&gt;
&lt;p&gt;This will slide the value of the slider to 70%.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;app.sliders.element.adjust(toNormalizedSliderPosition: 0.7)&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Interacting with pickers&lt;/h4&gt;
&lt;p&gt;A picker:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;app.pickerWheels.element.adjust(toPickerWheelValue: "Picker Wheel Item Title")&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Tapping links in web views&lt;/h4&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;app.links["Share this"].tap()&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Interactions&lt;/h3&gt;
&lt;h4&gt;Pull to refresh&lt;/h4&gt;
&lt;p&gt;You can simulate a pull-to-refresh action by swiping down on the screen.&lt;br&gt;
(if it is not refreshing, try calling it twice)&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;app.tables.swipeDown()&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Pushing and popping view controllers&lt;/h3&gt;
&lt;p&gt;Test if a view controller was pushed onto the navigation stack.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;app.buttons["More Info"].tap()
XCTAssert(app.navigationBars["Volleyball?"].exists)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pop a view controller by tapping the back button in the navigation bar and assert that the title in the navigation bar has changed.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;app.navigationBars.buttons.elementBoundByIndex(0).tap()
XCTAssert(app.navigationBars["Volley"].exists)&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Screenshots&lt;/h3&gt;
&lt;h4&gt;Taking a screenshot&lt;/h4&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;// Take a screenshot
let screenshot = XCUIScreen.main.screenshot()

// Attach the screenshot to the test report
let fullScreenshotAttachment = XCTAttachment(screenshot: screenshot)

// Set the attachment's lifetime to keep it around beyond the test run
fullScreenshotAttachment.lifetime = .keepAlways

// Add the attachment to the test report
add(fullScreenshotAttachment)&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Setting Up UI Testing in Xcode&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Create a UI Test Target&lt;/strong&gt;: When you create a new project in Xcode, you can add a UI test target by selecting the "Include UI Tests" checkbox.&lt;br&gt;
If you already have a project, you can add a UI test target by going to &lt;code&gt;File &amp;gt; New &amp;gt; Target&lt;/code&gt; and selecting "UI Testing Bundle".&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Write Your First UI Test&lt;/strong&gt;: Xcode generates a template UI test file for you. Open this file and you'll see a basic test method.&lt;br&gt;
You can start writing your own tests by using the &lt;code&gt;XCUIApplication&lt;/code&gt; class to interact with your app.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import XCTest

class MyAppUITests: XCTestCase {

    func testExample() {
        let app = XCUIApplication()
        app.launch()

        // Interact with the UI
        let button = app.buttons["MyButton"]
        XCTAssertTrue(button.exists)
        button.tap()

        let label = app.staticTexts["MyLabel"]
        XCTAssertEqual(label.label, "Expected Text")
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Frameworks for UI Testing in Swift.&lt;/h2&gt;
&lt;h3&gt;&lt;a href="https://github.com/0xWDG/XCUITestHelper"&gt;XCUITestHelper&lt;/a&gt; Framework benefits&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://github.com/0xWDG/XCUITestHelper"&gt;XCUITestHelper&lt;/a&gt; by &lt;a href="https://github.com/0xWDG"&gt;Me&lt;/a&gt; I've written this framework because I was missing some (for me essential) features.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://0xwdg.github.io/XCUITestHelper/documentation/xcuitesthelper/xctest/xcuiapplication/setlanguage(to:)"&gt;XCUIApplication.setLanguage(to:)&lt;/a&gt;&lt;br&gt;
This sets the user-interface language of the application which you are running.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://0xwdg.github.io/XCUITestHelper/documentation/xcuitesthelper/xctest/xcuielement/wait(for:)"&gt;XCUIApplication.wait(for:)&lt;/a&gt;&lt;br&gt;
This wait for a defined period before continuing&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://0xwdg.github.io/XCUITestHelper/documentation/xcuitesthelper/xctest/xcuiapplication/navigateback()"&gt;XCUIApplication.navigateBack()&lt;/a&gt;&lt;br&gt;
This navigates back (if you are using a NavigationView)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://0xwdg.github.io/XCUITestHelper/documentation/xcuitesthelper/xctest/xcuielementquery/random"&gt;XCUIElementQuery.random&lt;/a&gt;&lt;br&gt;
This selects a random element&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://0xwdg.github.io/XCUITestHelper/documentation/xcuitesthelper/xctest/xcuielementquery/lastMatch"&gt;XCUIElementQuery.lastMatch&lt;/a&gt;&lt;br&gt;
This gives the last match of an predicate&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://0xwdg.github.io/XCUITestHelper/documentation/xcuitesthelper/xctest/xcuielementquery/subscript(_:)"&gt;XCUIElementQuery.subscript(_:)&lt;/a&gt;&lt;br&gt;
This supports that you can use &lt;code&gt;listOfElements[number]&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://0xwdg.github.io/XCUITestHelper/documentation/xcuitesthelper/xctest/xcuielement/type(in:text:action:)"&gt;XCUIElementQuery.type(in:text:action:)&lt;/a&gt;&lt;br&gt;
This types some text in the inputfield and can press continue/enter.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;a href="https://github.com/Tunous/XCAppTest"&gt;XCAppTest&lt;/a&gt; Framework benefits&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://github.com/Tunous/XCAppTest"&gt;XCAppTest&lt;/a&gt; by &lt;a href="https://github.com/Tunous"&gt;Łukasz Rutkowski&lt;/a&gt; adds a ton of extra functions for testing.&lt;br&gt;
I want to highlight those in particular:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://swiftpackageindex.com/tunous/xcapptest/0.14.0/documentation/xcapptest/xctest/xcuielement/tapwhenready(timeout:_:file:line:)"&gt;XCUIElement.tapWhenReady(timeout:_:file:line:)&lt;/a&gt;&lt;br&gt;
This taps the element when it is ready.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://swiftpackageindex.com/tunous/xcapptest/0.14.0/documentation/xcapptest/xctest/xcuielement/tapifexists(timeout:_:file:line:)"&gt;XCUIElement.tapIfExists(timeout:_:file:line:)&lt;/a&gt;&lt;br&gt;
This taps the element if it exists.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://swiftpackageindex.com/tunous/xcapptest/0.14.0/documentation/xcapptest/xctest/xcuielement/presswhenready(forduration:timeout:_:file:line:)"&gt;XCUIElement.pressWhenReady(forDuration:timeout:_:file:line:)&lt;/a&gt;&lt;br&gt;
This presses the element when it is ready.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://swiftpackageindex.com/tunous/xcapptest/0.14.0/documentation/xcapptest/xctest/xctestcase/run(_:function:block:)"&gt;XCTestCase.run(_:function:block:)&lt;/a&gt;&lt;br&gt;
This runs a function on the element.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Bonus: Taking screenshots for the App Store&lt;/h2&gt;
&lt;p&gt;Taking screenshots for the App Store can be a tedious task.&lt;br&gt;
It is possible to automate this with UI tests.&lt;br&gt;
I refer to a &lt;a href="https://blog.winsmith.de/english/ios/2020/04/14/xcuitest-screenshots.html"&gt;blog post&lt;/a&gt; of my friend &lt;a href="https://social.telemetrydeck.com/@daniel"&gt;Daniel&lt;/a&gt; for this.&lt;/p&gt;
&lt;p&gt;Daniel uses this function to name the screenshots:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;func takeScreenshot(named name: String) {
    // Take the screenshot
    let fullScreenshot = XCUIScreen.main.screenshot()

    // Create a new attachment to save our screenshot
    // and give it a name consisting of the "named"
    // parameter and the device name, so we can find
    // it later.
    let screenshotAttachment = XCTAttachment(
        uniformTypeIdentifier: "public.png", 
        name: "Screenshot-\(UIDevice.current.name)-\(name).png",
        payload: fullScreenshot.pngRepresentation, 
        userInfo: nil)

    // Usually Xcode will delete attachments after 
    // the test has run; we don't want that!
    screenshotAttachment.lifetime = .keepAlways

    // Add the attachment to the test log, 
    // so we can retrieve it later
    add(screenshotAttachment)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With a bash script, you can run the tests on different simulators, languages, and appearances.&lt;br&gt;
This script will create a folder structure with the screenshots for each combination.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;#!/bin/bash

# The Xcode project to create screenshots for
projectName="./Libi.xcodeproj"

# The scheme to run tests for
schemeName="Libi"

# All the simulators we want to screenshot
# Copy/Paste new names from Xcode's
# "Devices and Simulators" window
# or from `xcrun simctl list`.
simulators=(
    "iPhone 8"
    "iPhone 11 Pro"
    "iPhone 11 Pro Max"
    "iPad Pro (12.9-inch) (3rd generation)"
    "iPad Pro (9.7-inch)"
)

# All the languages we want to screenshot (ISO 3166-1 codes)
languages=(
    "en"
    "de"
    "fr"
)

# All the appearances we want to screenshot
# (options are "light" and "dark")
appearances=(
    "light"
    "dark"
)

# Save final screenshots into this folder (it will be created)
targetFolder="/Users/breakthesystem/Desktop/LibiScreenshots"

## No need to edit anything beyond this point
for simulator in "${simulators[@]}"
do
    for language in "${languages[@]}"
    do
        for appearance in "${appearances[@]}"
        do
            rm -rf /tmp/LibiDerivedData/Logs/Test
            echo "📲  Building and Running for $simulator in $language"

            # Boot up the new simulator and set it to 
            # the correct appearance
            xcrun simctl boot "$simulator"
            xcrun simctl ui "$simulator" appearance $appearance

            # Build and Test
            xcodebuild \
                -testLanguage $language \
                -scheme $schemeName \
                -project $projectName \
                -derivedDataPath '/tmp/LibiDerivedData/' \
                -destination "platform=iOS Simulator,name=$simulator" \
                build test
            echo "🖼  Collecting Results..."
            mkdir -p "$targetFolder/$simulator/$language/$appearance"
            find /tmp/LibiDerivedData/Logs/Test -maxdepth 1 -type d -exec xcparse screenshots {} "$targetFolder/$simulator/$language/$appearance" \;
        done
    done

    echo "✅  Done"
done&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;UI testing is an essential part of ensuring your app's quality and reliability.&lt;br&gt;
By writing effective UI tests in Swift, you can catch bugs early, improve your app's user experience, and gain confidence in your code.&lt;br&gt;
With the XCTest framework and the tips provided in this guide, you'll be well on your way to mastering UI testing in Swift.&lt;/p&gt;
&lt;h3&gt;Resources:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/0xWDG/XCUITestHelper"&gt;XCUITestHelper&lt;/a&gt; by &lt;a href="https://github.com/0xWDG"&gt;Wesley de Groot&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/Tunous/XCAppTest"&gt;XCAppTest&lt;/a&gt; by &lt;a href="https://github.com/Tunous"&gt;Łukasz Rutkowski&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/joemasilotti/UI-Testing-Cheat-Sheet"&gt;UI Testing Cheat Sheet&lt;/a&gt; by &lt;a href="https://github.com/joemasilotti"&gt;Joe Masilotti&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://blog.winsmith.de/english/ios/2020/04/14/xcuitest-screenshots.html"&gt;Blog post: Creating automated Screenshots using XCUITest&lt;/a&gt; by &lt;a href="https://social.telemetrydeck.com/@daniel"&gt;Daniel&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Thanks:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Thanks &lt;a href="https://mastodon.world/@luckkerr"&gt;Łukasz&lt;/a&gt; for &lt;a href="https://mastodon.world/@luckkerr/112942359863382376"&gt;mentioning XCAppTest&lt;/a&gt;!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Thanks &lt;a href="https://social.telemetrydeck.com/@daniel"&gt;Daniel&lt;/a&gt; for your &lt;a href="https://blog.winsmith.de/english/ios/2020/04/14/xcuitest-screenshots.html"&gt;Blog post&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nb: This post is using different headings and is written in a different style than the other posts.&lt;br&gt;
Let me know if you like it or not.&lt;/p&gt;</description>
  </item>
  <item>
    <title>Understanding assertions in Swift</title>    <link>https://wesleydegroot.nl/blog/understanding-assertions-in-swift</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/understanding-assertions-in-swift</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:58 +0200</pubDate>
    <category>Assert</category>
    <category>Swift</category>
    <description>&lt;p&gt;Assertions are a powerful tool in Swift that help developers ensure their code behaves as expected during runtime. They allow you to test assumptions and catch potential issues early in the development process. In this blog post, we'll explore what assertions are, how to use them, and the different types available in Swift.&lt;/p&gt;
&lt;h4&gt;What Are Assertions?&lt;/h4&gt;
&lt;p&gt;Assertions are runtime checks that verify whether a condition is true. If the condition evaluates to false, the program terminates and prints an error message. This helps developers identify and fix bugs early, ensuring that the code behaves as intended.&lt;/p&gt;
&lt;h4&gt;Why Use Assertions?&lt;/h4&gt;
&lt;p&gt;Using assertions in your code can help:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Catch Bugs Early&lt;/strong&gt;: By verifying assumptions, you can catch potential issues before they become harder to debug.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Improve Code Quality&lt;/strong&gt;: Assertions make your code more robust and reliable by ensuring that it meets certain conditions.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Simplify Debugging&lt;/strong&gt;: When an assertion fails, it provides a clear indication of what went wrong, making it easier to pinpoint the issue.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Types of Assertions in Swift&lt;/h4&gt;
&lt;p&gt;Swift provides several types of assertions, each serving a different purpose:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;assert()&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Usage&lt;/strong&gt;: &lt;code&gt;assert(condition, "Message")&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Description&lt;/strong&gt;: Checks if the condition is true. If not, it prints the message and terminates the program.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Terminates app in release mode&lt;/strong&gt;: No.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;When to use&lt;/strong&gt;: Use &lt;code&gt;assert()&lt;/code&gt; to verify assumptions that are only relevant during development.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;let age = 15
assert(age &amp;gt;= 18, "Access denied - You must be at least 18 years old.")&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;assertionFailure()&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Usage&lt;/strong&gt;: &lt;code&gt;assertionFailure("Message")&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Description&lt;/strong&gt;: Always triggers a failure, printing the message and terminating the program.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Terminates app in release mode&lt;/strong&gt;: No.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;When to use&lt;/strong&gt;: Use &lt;code&gt;assertionFailure()&lt;/code&gt; to indicate that a specific code path should never be reached.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;assertionFailure("This code should never be reached.")&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;precondition()&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Usage&lt;/strong&gt;: &lt;code&gt;precondition(condition, "Message")&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Description&lt;/strong&gt;: Checks if the condition is true. If not, it prints the message and terminates the program.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Terminates app in release mode&lt;/strong&gt;: Yes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;When to use&lt;/strong&gt;: Use &lt;code&gt;precondition()&lt;/code&gt; for critical conditions that should never be violated.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;let index = -1
precondition(index &amp;gt;= 0, "Index must be non-negative.")&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;preconditionFailure()&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Usage&lt;/strong&gt;: &lt;code&gt;preconditionFailure("Message")&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Description&lt;/strong&gt;: Always triggers a failure, printing the message and terminating the program.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Terminates app in release mode&lt;/strong&gt;: Yes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;preconditionFailure("This code should never be reached.")&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;fatalError()&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Usage&lt;/strong&gt;: &lt;code&gt;fatalError("Message")&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Description&lt;/strong&gt;: Unconditionally terminates the program and prints the message. Use it for serious errors that should never occur.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Terminates app in release mode&lt;/strong&gt;: Yes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;When to use&lt;/strong&gt;: Use &lt;code&gt;fatalError()&lt;/code&gt; for unrecoverable errors that require immediate termination of the program.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;fatalError("Unrecoverable error occurred.")&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;When to Use Assertions&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Debugging&lt;/strong&gt;: Use &lt;code&gt;assert()&lt;/code&gt; and &lt;code&gt;assertionFailure()&lt;/code&gt; during development to catch bugs early.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Critical Conditions&lt;/strong&gt;: Use &lt;code&gt;precondition()&lt;/code&gt; and &lt;code&gt;preconditionFailure()&lt;/code&gt; for conditions that must always be true, even in production.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Unrecoverable Errors&lt;/strong&gt;: Use &lt;code&gt;fatalError()&lt;/code&gt; for serious issues that require immediate termination of the program.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;Assertions are a valuable tool in Swift that help ensure your code behaves as expected. By using assertions effectively, you can catch bugs early, improve code quality, and simplify debugging. Remember to choose the appropriate type of assertion based on the context and build configuration.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swift/debugging-and-reflection"&gt;https://developer.apple.com/documentation/swift/debugging-and-reflection&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Safely unwrap optional values in SwiftUI bindings</title>    <link>https://wesleydegroot.nl/blog/safely-unwrap-optional-values-in-swiftui-bindings</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/safely-unwrap-optional-values-in-swiftui-bindings</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:59 +0200</pubDate>
    <category>SwiftUI</category>
    <category>Binding</category>
    <description>&lt;p&gt;Bindings are a powerful feature in SwiftUI that allow you to connect your views to your data. However, when working with optional values, you need to be careful to avoid crashes due to force unwrapping. In this blog post, we'll explore how to safely unwrap optional values in SwiftUI bindings using the &lt;code&gt;Binding&lt;/code&gt; initializer and the nil-coalescing operator.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;Binding&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;Bindings in SwiftUI are a way to create a two-way connection between a view and a piece of data. They allow you to update the data from the view and reflect the changes in the view when the data changes. Bindings are typically used with &lt;code&gt;@State&lt;/code&gt; properties to create reactive UIs.&lt;/p&gt;
&lt;h2&gt;The Problem with Optional Values&lt;/h2&gt;
&lt;p&gt;You can't directly bind an optional value to a view in SwiftUI because bindings require non-optional values. If you try to bind an optional value directly, you'll get a compiler error. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;@State var optionalValue: Bool?

var body: some View {
    Switch(isOn: $optionalValue) { // Error: Cannot convert value of type 'Binding&amp;lt;Bool?&amp;gt;' to expected argument type 'Binding&amp;lt;Bool&amp;gt;'
        Text("Toggle")
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To work around this issue, you need to safely unwrap the optional value before creating the binding.&lt;/p&gt;
&lt;h2&gt;Safely Unwrapping Optional Values&lt;/h2&gt;
&lt;p&gt;To safely unwrap an optional value in a SwiftUI binding, you unwrap the optional value using a if-let statement and then create the binding. Here's an example:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct Person: Identifiable {
    let id: String
    var name: String
    var premiumEndDate: Date
}

@Observable
final class dataProvider {
    var person: Person?
}

struct ContentView: View {
    @State
    private var data: dataProvider = .init()

    var body: some View {
        // We safely unwrap the optional value before creating the new binding
        if let unwrapped = Binding($data.person) {
            // Use the unwrapped binding here
            Text("Premium end date is \(unwrapped.premiumEndDate)")
        } else {
            // Handle the case where the optional value is nil
            Text("Person is not a premium user")
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Using the Nil-Coalescing Operator&lt;/h2&gt;
&lt;p&gt;Another way to safely unwrap optional values in SwiftUI bindings is to use the nil-coalescing operator (&lt;code&gt;??&lt;/code&gt;).&lt;br&gt;
This operator allows you to provide a default value in case the optional value is nil. &lt;/p&gt;
&lt;p&gt;This is not supported by default for Bindings, but you can use the following workaround to achieve the same result.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

func ??&amp;lt;T&amp;gt;(lhs: Binding&amp;lt;Optional&amp;lt;T&amp;gt;&amp;gt;, rhs: T) -&amp;gt; Binding&amp;lt;T&amp;gt; {
    Binding(
        get: { lhs.wrappedValue ?? rhs },
        set: { lhs.wrappedValue = $0 }
    )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here's an example:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ContentView: View {
    @State
    private var optionalValue: Bool?

    var body: some View {
        // Use the nil-coalescing operator to provide a default value
        let unwrappedValue = $optionalValue ?? true

        Toggle(isOn: unwrappedValue) {
            Text("Toggle")
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;p&gt;When using the nil-coalescing operator to unwrap optional values in SwiftUI bindings, keep in mind that the default value you provide will be used if the optional value is nil. Make sure the default value is appropriate for your use case.&lt;/p&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;In this blog post, we explored how to safely unwrap optional values in SwiftUI bindings using the &lt;code&gt;Binding&lt;/code&gt; initializer and the nil-coalescing operator. By safely unwrapping optional values, you can avoid crashes due to force unwrapping and create more robust SwiftUI views.&lt;/p&gt;
&lt;p&gt;If you have any questions or comments, please feel free to leave them below.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swift/optional"&gt;https://developer.apple.com/documentation/swift/optional&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/binding"&gt;https://developer.apple.com/documentation/swiftui/binding&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/toggle"&gt;https://developer.apple.com/documentation/swiftui/toggle&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/text"&gt;https://developer.apple.com/documentation/swiftui/text&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://docs.swift.org/swift-book/documentation/the-swift-programming-language/generics/"&gt;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/generics/&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Hacktoberfest 2024</title>    <link>https://wesleydegroot.nl/blog/hacktoberfest-2024</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/hacktoberfest-2024</guid>
    <pubDate>Fri, 17 Oct 2025 22:36:59 +0200</pubDate>
    <category>Hacktoberfest</category>
    <description>&lt;p&gt;As the leaves change color and the air turns crisp, developers around the world gear up for one of the most exciting events in the open-source community: &lt;strong&gt;Hacktoberfest&lt;/strong&gt;.&lt;br&gt;
This annual celebration, organized by DigitalOcean, GitHub, and other partners, encourages developers to contribute to open-source projects throughout October.&lt;br&gt;
If you're a (Swift) developer, Hacktoberfest is the perfect opportunity to dive into the world of open source, enhance your skills, and make meaningful contributions.&lt;br&gt;
Let's explore how you can make the most of Hacktoberfest as a (Swift) developer.&lt;/p&gt;
&lt;h4&gt;What is Hacktoberfest?&lt;/h4&gt;
&lt;p&gt;Hacktoberfest is a month-long event that promotes open-source contributions.&lt;br&gt;
Participants are encouraged to submit pull requests (PRs) to open-source repositories on GitHub.&lt;br&gt;
By completing a certain number of PRs, participants can earn rewards.&lt;/p&gt;
&lt;h4&gt;Why Should Swift Developers Participate?&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Skill Enhancement&lt;/strong&gt;: Contributing to open-source projects allows you to work on real-world problems, enhancing your coding skills and learning new techniques.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Community Engagement&lt;/strong&gt;: Hacktoberfest is a great way to connect with other developers, share knowledge, and collaborate on exciting projects.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Portfolio Building&lt;/strong&gt;: Contributions to open-source projects are a valuable addition to your portfolio, showcasing your skills to potential employers.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Giving Back&lt;/strong&gt;: Open source thrives on community contributions. By participating, you help improve the tools and libraries that you and others use daily.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;Getting Started with Hacktoberfest&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Sign Up&lt;/strong&gt;: Register for Hacktoberfest on the &lt;a href="https://hacktoberfest.com/"&gt;official website&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Find Projects&lt;/strong&gt;: Look for Swift projects that need contributions. You can filter repositories by language on GitHub to find Swift-specific projects.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Read the Guidelines&lt;/strong&gt;: Each project has its own contribution guidelines. Make sure to read and follow them to ensure your PRs are accepted.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Start Contributing&lt;/strong&gt;: Begin by tackling issues labeled "Hacktoberfest" or "good first issue." These are typically beginner-friendly and a great way to get started.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;Tips for Successful Contributions&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Communicate&lt;/strong&gt;: Engage with project maintainers and other contributors. Ask questions if you're unsure about something.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Write Clear PRs&lt;/strong&gt;: Ensure your pull requests are well-documented and clearly explain the changes you've made.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Test Thoroughly&lt;/strong&gt;: Make sure your code works as expected and doesn't introduce new bugs.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Be Respectful&lt;/strong&gt;: Open-source communities thrive on respect and collaboration. Be courteous and constructive in your interactions.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Recommended Swift Projects for Hacktoberfest&lt;/h4&gt;
&lt;p&gt;Here are a few Swift projects that welcome contributions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/AuroraEditor/AuroraEditor"&gt;AuroraEditor&lt;/a&gt;: Aurora Editor is a IDE built by the community, for the community, and written in Swift for the best native performance and feel for macOS.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/realm/SwiftLint"&gt;SwiftLint&lt;/a&gt;: A tool to enforce Swift style and conventions.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/vapor/vapor"&gt;Vapor&lt;/a&gt;: A web framework for Swift.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;All of &lt;a href="https://github.com/stars/0xWDG/lists/my-spm-packages"&gt;my SPM projects&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Search on Hacktoberfest on &lt;a href="https://github.com/topics/hacktoberfest"&gt;GitHub&lt;/a&gt;, &lt;a href="https://gitlab.com/explore/projects/topics/hacktoberfest"&gt;GitLab&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;Hacktoberfest is a fantastic opportunity for Swift developers to contribute to the open-source community, learn new skills, and connect with like-minded individuals. Whether you're a seasoned developer or just starting out, there's a place for you in Hacktoberfest. So, roll up your sleeves, dive into some code, and make this October a month of meaningful contributions.&lt;/p&gt;
&lt;p&gt;For more information and to register, visit the &lt;a href="https://hacktoberfest.com/"&gt;Hacktoberfest website&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy Hacktoberfest!&lt;/p&gt;</description>
  </item>
  <item>
    <title>SwiftUI Development with DynamicUI</title>    <link>https://wesleydegroot.nl/blog/swift-package-dynamicui</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/swift-package-dynamicui</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:00 +0200</pubDate>
    <category>SwiftPM</category>
    <category>DynamicUI</category>
    <description>&lt;p&gt;In the ever-evolving landscape of mobile app development, flexibility and efficiency are key.&lt;br&gt;
One tool that stands out in achieving these goals is &lt;strong&gt;DynamicUI&lt;/strong&gt;, a Swift Package designed to create SwiftUI user interfaces dynamically from JSON files.&lt;br&gt;
Let's explore how DynamicUI can transform your development process.&lt;/p&gt;
&lt;h4&gt;What is DynamicUI?&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;DynamicUI&lt;/strong&gt; is a Swift Package that allows developers to define their SwiftUI user interfaces using JSON files.&lt;br&gt;
This approach enables the creation of dynamic and flexible UIs without the need for hardcoding every element.&lt;/p&gt;
&lt;h4&gt;Key Features&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;JSON-Driven UI&lt;/strong&gt;: DynamicUI reads JSON files to construct SwiftUI views, making it easy to update and modify the UI without changing the code.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Flexibility&lt;/strong&gt;: By using JSON to define the UI, developers can quickly adapt to changing requirements and design specifications¹.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Ease of Integration&lt;/strong&gt;: Adding DynamicUI to your project is straightforward. You can include it as a dependency in your &lt;code&gt;Package.swift&lt;/code&gt; file and start using it with minimal setup¹.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;How to Use DynamicUI&lt;/h4&gt;
&lt;p&gt;Integrating DynamicUI into your SwiftUI project is simple. Here’s a quick example to get you started:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import DynamicUI

struct ContentView: View {
    var body: some View {
        if let jsonURL = Bundle.main.url(forResource: "layout", withExtension: "json"),
           let jsonData = try? Data(contentsOf: jsonURL),
           let dynamicView = try? DynamicUI(jsonData: jsonData) {
            dynamicView
        } else {
            Text("Failed to load UI")
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, &lt;code&gt;DynamicUI&lt;/code&gt; reads a JSON file named &lt;code&gt;layout.json&lt;/code&gt; from the app bundle and constructs the corresponding SwiftUI view. This approach allows for rapid UI changes by simply updating the JSON file.&lt;/p&gt;
&lt;h4&gt;Benefits of Using DynamicUI&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Rapid Prototyping&lt;/strong&gt;: With DynamicUI, you can quickly prototype different UI layouts by modifying the JSON file, making it ideal for iterative design processes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Reduced Code Complexity&lt;/strong&gt;: By defining the UI in JSON, you can keep your Swift codebase clean and focused on business logic rather than UI details.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Enhanced Collaboration&lt;/strong&gt;: Designers and developers can work more closely together, with designers providing JSON files that developers can directly use to build the UI.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;UI Updates from the Cloud&lt;/strong&gt;: DynamicUI can be extended to fetch JSON files from a server, enabling real-time updates to the UI without requiring app updates¹.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Caveats&lt;/h4&gt;
&lt;p&gt;As of writing, DynamicUI does support (basic) support for callbacks, and not all modifiers are supported yet, i'd recommend to check out the &lt;a href="https://github.com/0xWDG/DynamicUI"&gt;GitHub repository&lt;/a&gt; to find out if your feature is working, and i'd love if you can make a contribution if you see something what &lt;em&gt;you&lt;/em&gt; can improve.&lt;/p&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;DynamicUI is a powerful tool for any SwiftUI developer looking to enhance their development workflow. By leveraging JSON to define user interfaces, DynamicUI offers a flexible and efficient way to create dynamic and responsive UIs.&lt;/p&gt;
&lt;p&gt;For more details and to get started with DynamicUI, visit the &lt;a href="https://github.com/0xWDG/DynamicUI"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;</description>
  </item>
  <item>
    <title>Simplifying multiplatform Colors</title>    <link>https://wesleydegroot.nl/blog/swift-package-colors</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/swift-package-colors</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:01 +0200</pubDate>
    <category>SwiftPM</category>
    <category>Colors</category>
    <description>&lt;p&gt;Sometimes you are building for multiple platforms and you want to use the same color across all of them.&lt;br&gt;
This is where the &lt;a href="https://github.com/0xWDG/Colors"&gt;&lt;code&gt;Colors&lt;/code&gt;&lt;/a&gt; package comes in handy.&lt;br&gt;
It provides a set of predefined system colors that you can use in your SwiftUI projects.&lt;/p&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;p&gt;To use the &lt;a href="https://github.com/0xWDG/Colors"&gt;&lt;code&gt;Colors&lt;/code&gt;&lt;/a&gt; package in your project, add the following dependency to your &lt;code&gt;Package.swift&lt;/code&gt; file:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;dependencies: [
    .package(url: "https://github.com/0xWDG/Colors", branch: "main")
]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, add &lt;code&gt;Colors&lt;/code&gt; as a dependency for your target:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;targets: [
    .target(
        name: "YourTarget",
        dependencies: [
            .product(name: "Colors", package: "Colors")
        ]
    )
]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, import the &lt;code&gt;Colors&lt;/code&gt; module in your Swift files:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import Colors&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use the predefined colors in your SwiftUI views:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;
import SwiftUI
import Colors

struct ContentView: View {
    var body: some View {
        VStack {
            Text("Hello, World!")
                .foregroundColor(Color.label)
            Text("Hello, World!")
                .foregroundColor(Color.systemTeal)
            Text("Hello, World!")
                .foregroundColor(Color.systemGray3)
            Text("Hello, World!")
                .foregroundColor(Color.quaternaryLabel)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Which colors are available?&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Color&lt;/th&gt;
&lt;th&gt;Light Mode&lt;/th&gt;
&lt;th&gt;Dark Mode&lt;/th&gt;
&lt;th&gt;(Original) Platforms&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/1623401-lighttext"&gt;.lightText&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ffffffff'&gt;#ffffffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ffffffff'&gt;#ffffffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/1623400-darktext"&gt;.darkText&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#000000ff'&gt;#000000ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#000000ff'&gt;#000000ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3173134-placeholdertext"&gt;.placeholderText&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#3d3d42ff'&gt;#3d3d42ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#eaeaf4ff'&gt;#eaeaf4ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3173131-label"&gt;.label&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#000000ff'&gt;#000000ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ffffffff'&gt;#ffffffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS, tvOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3173136-secondarylabel"&gt;.secondaryLabel&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#3d3d42ff'&gt;#3d3d42ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#eaeaf4ff'&gt;#eaeaf4ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS, tvOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3173153-tertiarylabel"&gt;.tertiaryLabel&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#3d3d42ff'&gt;#3d3d42ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#eaeaf4ff'&gt;#eaeaf4ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS, tvOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3173135-quaternarylabel"&gt;.quaternaryLabel&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#3d3d42ff'&gt;#3d3d42ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#eaeaf4ff'&gt;#eaeaf4ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS, tvOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3173140-systembackground"&gt;.systemBackground&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ffffffff'&gt;#ffffffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#000000ff'&gt;#000000ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3173137-secondarysystembackground"&gt;.secondarySystemBackground&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#f2f2f7ff'&gt;#f2f2f7ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#1c1c1eff'&gt;#1c1c1eff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3173154-tertiarysystembackground"&gt;.tertiarySystemBackground&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ffffffff'&gt;#ffffffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#2b2b2dff'&gt;#2b2b2dff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3255070-systemfill"&gt;.systemFill&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#77777fff'&gt;#77777fff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#77777fff'&gt;#77777fff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3255069-secondarysystemfill"&gt;.secondarySystemFill&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#77777fff'&gt;#77777fff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#77777fff'&gt;#77777fff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3255076-tertiarysystemfill"&gt;.tertiarySystemFill&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#75757fff'&gt;#75757fff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#75757fff'&gt;#75757fff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3255068-quaternarysystemfill"&gt;.quaternarySystemFill&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#72727fff'&gt;#72727fff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#75757fff'&gt;#75757fff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3173145-systemgroupedbackground"&gt;.systemGroupedBackground&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#f2f2f7ff'&gt;#f2f2f7ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#000000ff'&gt;#000000ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3173138-secondarysystemgroupedbackground"&gt;.secondarySystemGroupedBackground&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#1c1c1eff'&gt;#1c1c1eff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ffffffff'&gt;#ffffffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3173155-tertiarysystemgroupedbackground"&gt;.tertiarySystemGroupedBackground&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#f2f2f7ff'&gt;#f2f2f7ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#2b2b2dff'&gt;#2b2b2dff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3173143-systemgray"&gt;.systemGray&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#8e8e93ff'&gt;#8e8e93ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#8e8e93ff'&gt;#8e8e93ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS, tvOS, visionOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3255071-systemgray2"&gt;.systemGray2&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#adadb2ff'&gt;#adadb2ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#636366ff'&gt;#636366ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS, visionOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3255072-systemgray3"&gt;.systemGray3&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#c6c6ccff'&gt;#c6c6ccff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#474749ff'&gt;#474749ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS, visionOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3255073-systemgray4"&gt;.systemGray4&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#d1d1d6ff'&gt;#d1d1d6ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#3a3a3dff'&gt;#3a3a3dff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS, visionOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3255074-systemgray5"&gt;.systemGray5&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#e5e5eaff'&gt;#e5e5eaff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#2b2b2dff'&gt;#2b2b2dff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS, visionOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3255075-systemgray6"&gt;.systemGray6&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#f2f2f7ff'&gt;#f2f2f7ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#1c1c1eff'&gt;#1c1c1eff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS, visionOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3173139-separator"&gt;.separator&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#3d3d42ff'&gt;#3d3d42ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#545459ff'&gt;#545459ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS, tvOS, visionOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3173133-opaqueseparator"&gt;.opaqueSeparator&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#c6c6c6ff'&gt;#c6c6c6ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#38383aff'&gt;#38383aff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS, tvOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3173132-link"&gt;.link&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#007affff'&gt;#007affff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#0a84ffff'&gt;#0a84ffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS, tvOS, visionOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3173141-systemblue"&gt;.systemBlue&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#007affff'&gt;#007affff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#0a84ffff'&gt;#0a84ffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS, tvOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3852740-systemcyan"&gt;.systemCyan&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#32ade6ff'&gt;#32ade6ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#64d2ffff'&gt;#64d2ffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS, tvOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3852741-systemmint"&gt;.systemMint&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#00c7beff'&gt;#00c7beff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#63e6e2ff'&gt;#63e6e2ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS, tvOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3173149-systempurple"&gt;.systemPurple&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#af51ddff'&gt;#af51ddff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#bf59f2ff'&gt;#bf59f2ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS, tvOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3173144-systemgreen"&gt;.systemGreen&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#33c659ff'&gt;#33c659ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#30d159ff'&gt;#30d159ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS, tvOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3173152-systemyellow"&gt;.systemYellow&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ffcc00ff'&gt;#ffcc00ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ffd60aff'&gt;#ffd60aff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS, tvOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3173147-systemorange"&gt;.systemOrange&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ff9300ff'&gt;#ff9300ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ff9e0aff'&gt;#ff9e0aff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS, tvOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3173148-systempink"&gt;.systemPink&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ff2d54ff'&gt;#ff2d54ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ff385eff'&gt;#ff385eff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS, tvOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3173150-systemred"&gt;.systemRed&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ff3a30ff'&gt;#ff3a30ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ff443aff'&gt;#ff443aff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS, tvOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3173151-systemteal"&gt;.systemTeal&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#59c6f9ff'&gt;#59c6f9ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#63d1ffff'&gt;#63d1ffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS, tvOS, macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3173146-systemindigo"&gt;.systemIndigo&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#5956d6ff'&gt;#5956d6ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#5e5be5ff'&gt;#5e5be5ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS, tvOS, macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/appkit/nscolor/2646931-scrubbertexturedbackground"&gt;.scrubberTexturedBackground&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ffffffff'&gt;#ffffffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ffffffff'&gt;#ffffffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/appkit/nscolor/1535446-textbackgroundcolor"&gt;.textBackgroundColor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ffffffff'&gt;#ffffffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#1e1e1eff'&gt;#1e1e1eff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/appkit/nscolor/1535230-controltextcolor"&gt;.controlTextColor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#000000ff'&gt;#000000ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ffffffff'&gt;#ffffffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/appkit/nscolor/1534635-quaternarylabelcolor"&gt;.quaternaryLabelColor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#000000ff'&gt;#000000ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ffffffff'&gt;#ffffffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/appkit/nscolor/2998827-findhighlightcolor"&gt;.findHighlightColor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ffff00ff'&gt;#ffff00ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ffff00ff'&gt;#ffff00ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/appkit/nscolor/1527697-highlightcolor"&gt;.highlightColor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ffffffff'&gt;#ffffffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#b5b5b5ff'&gt;#b5b5b5ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/appkit/nscolor/1525121-shadowcolor"&gt;.shadowColor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#000000ff'&gt;#000000ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#000000ff'&gt;#000000ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/appkit/nscolor/1524257-windowframetextcolor"&gt;.windowFrameTextColor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#000000ff'&gt;#000000ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ffffffff'&gt;#ffffffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/appkit/nscolor/1528630-windowbackgroundcolor"&gt;.windowBackgroundColor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#edededff'&gt;#edededff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#333333ff'&gt;#333333ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/appkit/nscolor/1532031-keyboardfocusindicatorcolor"&gt;.keyboardFocusIndicatorColor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#0066f4ff'&gt;#0066f4ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#19a8ffff'&gt;#19a8ffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/appkit/nscolor/2998831-separatorcolor"&gt;.separatorColor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#000000ff'&gt;#000000ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ffffffff'&gt;#ffffffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/appkit/nscolor/1526796-selectedcontrolcolor"&gt;.selectedControlColor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#b2d6ffff'&gt;#b2d6ffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#3f638cff'&gt;#3f638cff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/appkit/nscolor/1531948-controlbackgroundcolor"&gt;.controlBackgroundColor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ffffffff'&gt;#ffffffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#1e1e1eff'&gt;#1e1e1eff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/appkit/nscolor/1533254-secondarylabelcolor"&gt;.secondaryLabelColor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#000000ff'&gt;#000000ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ffffffff'&gt;#ffffffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/appkit/nscolor/1532376-tertiarylabelcolor"&gt;.tertiaryLabelColor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#000000ff'&gt;#000000ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ffffffff'&gt;#ffffffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/appkit/nscolor/1527240-gridcolor"&gt;.gridColor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#e5e5e5ff'&gt;#e5e5e5ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#191919ff'&gt;#191919ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/appkit/nscolor/1527413-alternateselectedcontroltextcolo"&gt;.alternateSelectedControlTextColor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ffffffff'&gt;#ffffffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ffffffff'&gt;#ffffffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/appkit/nscolor/2998832-unemphasizedselectedcontentbackg"&gt;.unemphasizedSelectedContentBackgroundColor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#dbdbdbff'&gt;#dbdbdbff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#444444ff'&gt;#444444ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/appkit/nscolor/1527025-textcolor"&gt;.textColor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#000000ff'&gt;#000000ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ffffffff'&gt;#ffffffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicolor/3173142-systembrown"&gt;.systemBrown&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#a3845eff'&gt;#a3845eff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#aa8e68ff'&gt;#aa8e68ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;iOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/appkit/nscolor/2998830-selectedcontentbackgroundcolor"&gt;.selectedContentBackgroundColor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#0063e0ff'&gt;#0063e0ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#0059d1ff'&gt;#0059d1ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/appkit/nscolor/1528605-selectedtextcolor"&gt;.selectedTextColor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#000000ff'&gt;#000000ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ffffffff'&gt;#ffffffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/appkit/nscolor/1534657-labelcolor"&gt;.labelColor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#000000ff'&gt;#000000ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ffffffff'&gt;#ffffffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/appkit/nscolor/2998829-placeholdertextcolor"&gt;.placeholderTextColor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#000000ff'&gt;#000000ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ffffffff'&gt;#ffffffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/appkit/nscolor/2998833-unemphasizedselectedtextbackgrou"&gt;.unemphasizedSelectedTextBackgroundColor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#dbdbdbff'&gt;#dbdbdbff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#444444ff'&gt;#444444ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/appkit/nscolor/1530573-disabledcontroltextcolor"&gt;.disabledControlTextColor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#000000ff'&gt;#000000ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ffffffff'&gt;#ffffffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/appkit/nscolor/1531237-headertextcolor"&gt;.headerTextColor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#000000ff'&gt;#000000ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ffffffff'&gt;#ffffffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/appkit/nscolor/2998828-linkcolor"&gt;.linkColor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#0068d8ff'&gt;#0068d8ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#3f9bffff'&gt;#3f9bffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/appkit/nscolor/1528136-selectedtextbackgroundcolor"&gt;.selectedTextBackgroundColor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#b2d6ffff'&gt;#b2d6ffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#3f638cff'&gt;#3f638cff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/appkit/nscolor/2998834-unemphasizedselectedtextcolor"&gt;.unemphasizedSelectedTextColor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#000000ff'&gt;#000000ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ffffffff'&gt;#ffffffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/appkit/nscolor/1524856-controlcolor"&gt;.controlColor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ffffffff'&gt;#ffffffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ffffffff'&gt;#ffffffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/appkit/nscolor/1535591-selectedcontroltextcolor"&gt;.selectedControlTextColor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#000000ff'&gt;#000000ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ffffffff'&gt;#ffffffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/appkit/nscolor/1534707-underpagebackgroundcolor"&gt;.underPageBackgroundColor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#969696ff'&gt;#969696ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#282828ff'&gt;#282828ff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/appkit/nscolor/1526658-selectedmenuitemtextcolor"&gt;.selectedMenuItemTextColor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ffffffff'&gt;#ffffffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style='background-color:#ffffffff'&gt;#ffffffff&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;style&gt;
    td &gt; span {
        display: table-cell;
        text-transform: uppercase;
        text-shadow: 0 0 1px #FFF, 0 0 1px #FFF;
    }
&lt;/style&gt;
&lt;!--
SystemCyan
SystemMint
--&gt;</description>
  </item>
  <item>
    <title>Simplifying App Onboarding with OnboardingKit</title>    <link>https://wesleydegroot.nl/blog/swift-package-onboardingkit</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/swift-package-onboardingkit</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:01 +0200</pubDate>
    <category>SwiftPM</category>
    <category>OnboardingKit</category>
    <description>&lt;p&gt;Creating a smooth and engaging onboarding experience is crucial for any app.&lt;br&gt;
OnboardingKit, a SwiftUI package available on GitHub, provides a comprehensive solution to help developers create these experiences effortlessly.&lt;br&gt;
In this article, we'll explore the features of OnboardingKit and how you can use it to enhance your app's onboarding process.&lt;/p&gt;
&lt;h4&gt;What is OnboardingKit?&lt;/h4&gt;
&lt;p&gt;OnboardingKit is a SwiftUI package designed to simplify the creation of onboarding experiences for iOS and macOS apps.&lt;br&gt;
It offers a set of customizable views that you can use to create welcome screens, "what's new" screens, and other onboarding screens.&lt;br&gt;
This package is particularly useful for developers looking to provide a seamless introduction to their app's features and updates.&lt;/p&gt;
&lt;h4&gt;Key Features&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Welcome Screen&lt;/strong&gt;: OnboardingKit allows you to create a welcome screen that can be displayed when the app is launched for the first time. This screen can include a series of slides, each highlighting different features of your app.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;What's New Screen&lt;/strong&gt;: You can use OnboardingKit to inform users about new features or updates in your app. This screen can be shown after an app update to keep users informed about the latest changes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Customizable Views&lt;/strong&gt;: The views provided by OnboardingKit are highly customizable. You can easily change the content, colors, and layout to match your app's design.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Helper Class&lt;/strong&gt;: OnboardingKit includes a helper class that provides information about your app, such as the app name, version number, and build number. This can be useful for displaying dynamic content in your onboarding screens.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;Installation&lt;/h4&gt;
&lt;p&gt;To install OnboardingKit, you can use Swift Package Manager. Add the following dependency to your &lt;code&gt;Package.swift&lt;/code&gt; file:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;dependencies: [
    .package(url: "https://github.com/0xWDG/OnboardingKit.git", .branch("main"))
]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, import OnboardingKit in your Swift files:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import OnboardingKit&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Welcome Screem&lt;/h4&gt;
&lt;p&gt;Here's a basic example of how to use OnboardingKit to create a welcome screen:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI
import OnboardingKit

struct ContentView: View {
    @State private var showWelcomeScreen = {
        if UserDefaults.standard.bool(forKey: "hasSeenIntroduction") {
            return false
        }
        return true
    }()

    private var features: [WelcomeCell] = [
        WelcomeCell(image: "star", title: "Welcome", subtitle: "To %APP_NAME%")
    ]

    var body: some View {
        TabView {
            // Your tab view code
        }
        .sheet(isPresented: $showWelcomeScreen) {
            WelcomeScreen(show: $showWelcomeScreen, items: features) {
                UserDefaults.standard.setValue(true, forKey: "hasSeenIntroduction")
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, the &lt;code&gt;WelcomeScreen&lt;/code&gt; view is displayed as a sheet when the app is launched for the first time.&lt;br&gt;
The &lt;code&gt;WelcomeCell&lt;/code&gt; struct is used to define the content of each slide in the welcome screen.&lt;/p&gt;
&lt;h4&gt;What's new screen&lt;/h4&gt;
&lt;p&gt;OnboardingKit also supports also  a "what's new" screen only, which you can pop up only when the app version changes:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI
import OnboardingKit

struct ContentView: View {
    @State private var showWhatsNew = {
        if let dictionary = Bundle.main.infoDictionary,
           let dVersion = dictionary["CFBundleShortVersionString"] as? String,
           let whatsNew = UserDefaults.standard.value(forKey: "whatsNew") as? String,
           whatsNew == dVersion {
            return false
        }
        return true
    }()

    var body: some View {
        TabView {
            // Your tab view code
        }
        .sheet(isPresented: $showWhatsNew) {
            WhatsNew(show: $showWhatsNew, text: "This is new!")
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, the &lt;code&gt;WhatsNew&lt;/code&gt; view is displayed only if the app version has changed since the last launch.&lt;/p&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;OnboardingKit is a powerful tool for creating engaging and informative onboarding experiences in your SwiftUI apps.&lt;br&gt;
By leveraging its customizable views and helper classes, you can ensure that your users have a smooth introduction to your app's features and updates.&lt;br&gt;
Whether you're building a welcome screen or a "what's new" screen, OnboardingKit makes the process straightforward and efficient.&lt;/p&gt;
&lt;p&gt;For more information and to get started with OnboardingKit, visit the &lt;a href="https://github.com/0xWDG/OnboardingKit"&gt;OnboardingKit GitHub repository&lt;/a&gt;.&lt;/p&gt;</description>
  </item>
  <item>
    <title>Simplifying Game Controller Integration with GameControllerKit</title>    <link>https://wesleydegroot.nl/blog/swift-package-gamecontrollerkit</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/swift-package-gamecontrollerkit</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:02 +0200</pubDate>
    <category>SwiftPM</category>
    <category>GameControllerKit</category>
    <description>&lt;p&gt;In the ever-evolving world of game development, integrating game controllers across multiple platforms can be a difficult task.&lt;br&gt;
Enter &lt;strong&gt;GameControllerKit&lt;/strong&gt;, a Swift package designed to streamline this process for developers working on iOS, macOS, and tvOS.&lt;br&gt;
Let's dive into what makes this toolkit a game-changer.&lt;/p&gt;
&lt;h3&gt;What is GameControllerKit?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;GameControllerKit&lt;/strong&gt; is an open-source Swift package that simplifies the process of connecting and interacting with game controllers.&lt;br&gt;
Whether you're developing for iOS, macOS, or tvOS, this toolkit provides a unified API to handle game controller inputs, control lights, and manage haptic feedback.&lt;/p&gt;
&lt;h3&gt;Key Features&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Easy Connection Management&lt;/strong&gt;: GameControllerKit makes it straightforward to interact with game controllers. The package handles the (dis)connections of game controllers, allowing developers to focus on gameplay rather than handling game controller conntections and setups.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Unified Input Handling&lt;/strong&gt;: With a simple and intuitive API, developers can easily read inputs from various game controllers. This includes buttons, joysticks, and other control mechanisms, ensuring a seamless gaming experience across different devices.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Advanced Control Options&lt;/strong&gt;: Beyond basic input handling, GameControllerKit allows developers to control the lights and haptics of connected controllers. This feature can be used to enhance the gaming experience by providing visual and tactile feedback to players.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Cross-Platform Support&lt;/strong&gt;: One of the standout features of GameControllerKit is its support for multiple Apple platforms. Whether you're developing a game for iOS, macOS, or tvOS, this toolkit ensures consistent controller behavior across all devices.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Getting Started&lt;/h3&gt;
&lt;p&gt;To get started with GameControllerKit, you can add it to your project using Swift Package Manager. Here’s a quick guide:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Add the Package&lt;/strong&gt;: In Xcode, go to &lt;code&gt;File &amp;gt; Swift Packages &amp;gt; Add Package Dependency&lt;/code&gt; and enter the repository URL: &lt;code&gt;https://github.com/0xWDG/GameControllerKit&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Import the Package&lt;/strong&gt;: In your Swift files, import GameControllerKit to access its features:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import GameControllerKit&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;React to Controllers&lt;/strong&gt;: Use the provided API to connect to game controllers and handle inputs:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;let controller = GameControllerKit()
gameController.set(handler: { button, pressed, controller in
    // Handle controller input
})&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Demo Application&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI
import GameControllerKit

struct ContentView: View {
    /// The game controller kit
    @State
    var gameController = GameControllerKit()

    /// Log
    @State
    var log: [String] = []

    var body: some View {
        VStack {
            Button {
                gameController.set(color: .GCKRandom)
            } label: {
                Text("Random Color")
            }

            Text("Controller: \(gameController.controller?.productCategory ?? "None"), " +
                 "\((gameController.controllerType ?? .generic).description)")
            Text("Last action:\n\(String(describing: gameController.lastAction)).")

            GCKControllerView()
                .environmentObject(gameController)

            List {
                ForEach(log.reversed(), id: \.self) { text in
                    Text(text)
                }
            }
        }
        .padding()
        .onAppear {
            gameController.set(handler: handler)
        }
    }

    /// Handler
    ///
    /// - Parameters:
    ///   - action: action
    ///   - pressed: is the button pressed?
    ///   - controller: which controller?
    public func handler(
        action: GCKAction,
        pressed: Bool,
        controller: GCKController
    ) {
        log.append(
            "\(String(describing: action))(\(action.position.arrowRepresentation)) \(pressed ? "Pressed" : "Unpressed"), " +
            "Controller #id \(String(describing: controller.playerIndex.rawValue))"
        )

        if action == .buttonA &amp;amp;&amp;amp; pressed {
            // set to a random color
            gameController.set(color: .GCKRandom)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Screenshots:&lt;/h3&gt;
&lt;h4&gt;iOS&lt;/h4&gt;
&lt;p&gt;&lt;img src="https://github.com/user-attachments/assets/7bae192c-41ae-42d5-ad52-e204de73b3a0" alt="C65552DF-04CC-493E-AD73-C385A7CEC53C" loading="lazy"&gt;&lt;/p&gt;
&lt;h4&gt;MacOS&lt;/h4&gt;
&lt;img width="1012" alt="AA801C52-88A1-4326-A5DC-3A04DF491077" src="https://github.com/user-attachments/assets/f6eaa752-fdda-4ddd-8eeb-acbc9797dc94"&gt;
&lt;h4&gt;tvOS&lt;/h4&gt;
&lt;p&gt;&lt;img src="https://github.com/user-attachments/assets/77def389-784e-44b5-9df8-80b675fdb8bf" alt="Screenshot 2024-08-29 at 14 43 51" loading="lazy"&gt;&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;GameControllerKit is a powerful tool for game developers looking to integrate game controllers into their projects with ease. Its simple API, cross-platform support, and advanced control options make it an invaluable resource for creating immersive gaming experiences. Whether you're a seasoned developer or just starting out, GameControllerKit can help you take your game to the next level.&lt;/p&gt;
&lt;p&gt;Check out the &lt;a href="https://github.com/0xWDG/GameControllerKit"&gt;GameControllerKit repository&lt;/a&gt; to learn more and start integrating it into your projects today!&lt;/p&gt;</description>
  </item>
  <item>
    <title>Enhancing SwiftUI with CachedAsyncImage</title>    <link>https://wesleydegroot.nl/blog/swift-package-cachedasyncimage</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/swift-package-cachedasyncimage</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:02 +0200</pubDate>
    <category>SwiftPM</category>
    <category>CachedAsyncImage</category>
    <description>&lt;p&gt;In the world of mobile app development, performance and user experience are paramount.&lt;br&gt;
One common challenge developers face is efficiently loading and caching images.&lt;br&gt;
Enter &lt;strong&gt;CachedAsyncImage&lt;/strong&gt;, a Swift Package designed to simplify this process.&lt;br&gt;
Let's dive into what makes CachedAsyncImage a valuable tool for iOS and macOS developers.&lt;/p&gt;
&lt;h4&gt;What is CachedAsyncImage?&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;CachedAsyncImage&lt;/strong&gt; is a Swift Package that extends the capabilities of SwiftUI's &lt;code&gt;AsyncImage&lt;/code&gt; by adding caching functionality. This means that images loaded from the web can be stored locally, reducing the need for repeated network requests and improving app performance.&lt;/p&gt;
&lt;h4&gt;Key Features&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Asynchronous Image Loading&lt;/strong&gt;: Just like &lt;code&gt;AsyncImage&lt;/code&gt;, CachedAsyncImage loads images asynchronously, ensuring that your app's UI remains responsive.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Built-in Caching&lt;/strong&gt;: By default, CachedAsyncImage uses &lt;code&gt;URLCache.shared&lt;/code&gt; to store images. This can be customized to use different caching mechanisms if needed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Simple Integration&lt;/strong&gt;: Integrating CachedAsyncImage into your project is straightforward. You can add it as a dependency in your &lt;code&gt;Package.swift&lt;/code&gt; file and start using it with minimal setup¹.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;How to Use CachedAsyncImage&lt;/h4&gt;
&lt;p&gt;Using CachedAsyncImage is as simple as replacing &lt;code&gt;AsyncImage&lt;/code&gt; with &lt;code&gt;CachedAsyncImage&lt;/code&gt; in your SwiftUI views.&lt;br&gt;
Here’s a quick example:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI
import CachedAsyncImage

struct ContentView: View {
    var body: some View {
        VStack {
            CachedAsyncImage(
                url: URL(
                    string: "https://wesleydegroot.nl/assets/avatar/avatar.webp"
                )
            ) { image in
                image
                    .resizable()
                    .frame(width: 250, height: 250)
            } placeholder: {
                ProgressView()
            }
        }
        .padding()
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or a more advanced example:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import CachedAsyncImage

struct ContentView: View {
    let imageURL = URL(string: "https://wesleydegroot.nl/assets/avatar/avatar.webp")!

    var body: some View {
        CachedAsyncImage(url: imageURL) { phase in
            switch phase {
            case .empty:
                ProgressView()
            case .success(let image):
                image
                    .resizable()
                    .aspectRatio(contentMode: .fit)
            case .failure:
                Image(systemName: "photo")
            @unknown default:
                EmptyView()
            }
        }
        .frame(width: 100, height: 100)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, &lt;code&gt;CachedAsyncImage&lt;/code&gt; handles the image loading and caching seamlessly.&lt;br&gt;
The &lt;code&gt;phase&lt;/code&gt; parameter allows you to manage different states (loading, success, failure) effectively.&lt;/p&gt;
&lt;h4&gt;Benefits of Using CachedAsyncImage&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Improved Performance&lt;/strong&gt;: By caching images, you reduce the number of network requests, leading to faster load times and a smoother user experience.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Reduced Data Usage&lt;/strong&gt;: Caching images locally means less data is consumed, which is beneficial for users on limited data plans.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Enhanced User Experience&lt;/strong&gt;: With faster image loading times, users are less likely to experience delays or interruptions while using your app.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;CachedAsyncImage is a powerful tool for any SwiftUI developer looking to enhance their app's performance and user experience. By integrating this package, you can ensure that images are loaded efficiently and cached effectively, providing a seamless experience for your users.&lt;/p&gt;
&lt;p&gt;For more details and to get started with CachedAsyncImage, visit the &lt;a href="https://github.com/0xWDG/CachedAsyncImage"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;Thanks&lt;/h4&gt;
&lt;p&gt;&lt;a href="https://github.com/0xWDG/CachedAsyncImage"&gt;CachedAsyncImage&lt;/a&gt; is based on &lt;a href="https://github.com/lorenzofiamingo/swiftui-cached-async-image"&gt;swiftui-cached-async-image&lt;/a&gt;.&lt;/p&gt;</description>
  </item>
  <item>
    <title>Inspect: A Powerful Tool for SwiftUI Developers</title>    <link>https://wesleydegroot.nl/blog/swift-package-inspect</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/swift-package-inspect</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:03 +0200</pubDate>
    <category>SwiftPM</category>
    <category>Inspect</category>
    <description>&lt;p&gt;As SwiftUI continues to evolve, developers are constantly seeking tools to streamline their workflow and enhance their debugging capabilities.&lt;br&gt;
One such tool that stands out is &lt;strong&gt;Inspect&lt;/strong&gt;, a Swift Package designed to provide deeper insights into SwiftUI views.&lt;br&gt;
Let's explore what makes Inspect an essential addition to your development toolkit.&lt;/p&gt;
&lt;h4&gt;What is Inspect?&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Inspect&lt;/strong&gt; is a Swift Package that allows developers to access the underlying *Kit elements of SwiftUI views.&lt;br&gt;
This capability is particularly useful for debugging and testing, as it provides a way to inspect and manipulate the internal components of SwiftUI views.&lt;/p&gt;
&lt;h4&gt;Key Features&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Deep Inspection&lt;/strong&gt;: Inspect enables you to delve into the underlying UIKit or AppKit elements of SwiftUI views, offering a granular level of detail that is not typically accessible¹.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Enhanced Debugging&lt;/strong&gt;: By exposing the internal structure of SwiftUI views, Inspect makes it easier to identify and resolve issues, leading to more robust and reliable applications¹.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;How to Use Inspect&lt;/h4&gt;
&lt;p&gt;Integrating Inspect into your SwiftUI project is simple. Here’s a quick example to get you started:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI
import Inspect

struct ContentView: View {
#if os(macOS)
    let PlatformImageView = NSImageView.self
#else
    let PlatformImageView = UIImageView.self
#endif

    var body: some View {
        VStack {
            Image(systemName: "star")
                .inspect(PlatformImageView) { view in
                    print(view)
                }
        }
        .padding()
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, &lt;code&gt;.inspect()&lt;/code&gt; is used to access the underlying &lt;code&gt;UIImageView&lt;/code&gt; or &lt;code&gt;NSImageView&lt;/code&gt; element of the &lt;code&gt;Image&lt;/code&gt; view.&lt;/p&gt;
&lt;p&gt;In the following example, &lt;code&gt;.inspectVC()&lt;/code&gt; is used to access the &lt;code&gt;UITabBarController&lt;/code&gt; or &lt;code&gt;NSTabViewController&lt;/code&gt; of a &lt;code&gt;List&lt;/code&gt; view:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI
import Inspect

struct ContentView: View {
    var body: some View {
        List {
            Text("Item 1")
            Text("Item 2")
            Text("Item 3")
            Text("Item 4")
            Text("Item 5")
        }
        .inspectVC({ $0.tabBarController }) { view
            print(view)
        }
        .padding()
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Benefits of Using Inspect&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Improved Debugging&lt;/strong&gt;: With the ability to inspect underlying elements, you can quickly identify and fix issues, leading to more stable and reliable applications.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Enhanced Testing&lt;/strong&gt;: Inspect provides a way to verify the internal state and structure of your views, making it easier to write comprehensive tests.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Greater Control&lt;/strong&gt;: By exposing the underlying elements, Inspect gives you more control over your SwiftUI views, allowing for more precise adjustments and customizations.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;Inspect is a powerful tool for any SwiftUI developer looking to enhance their debugging and testing capabilities. By providing access to the underlying *Kit elements of SwiftUI views, Inspect offers a level of insight and control that can significantly improve your development workflow.&lt;/p&gt;
&lt;p&gt;For more details and to get started with Inspect, visit the &lt;a href="https://github.com/0xWDG/Inspect"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;</description>
  </item>
  <item>
    <title>SwiftLeeds 2024 (day 1)</title>    <link>https://wesleydegroot.nl/blog/swiftleeds-2024-1</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/swiftleeds-2024-1</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:03 +0200</pubDate>
    <category>SwiftLeeds</category>
    <category>Conference</category>
    <description>&lt;p&gt;It's that time of year again!&lt;br&gt;
SwiftLeeds 2024 is here, and in this post, i'll be sharing my thoughts on the first day of the conference.&lt;/p&gt;
&lt;h4&gt;Agenda:&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;From Side Project to Going Indie - &lt;a href="https://www.avanderlee.com/"&gt;Antoine van der Lee&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Multi-Platform Libraries with Swift for WebAssembly - &lt;a href="https://mastodon.social/@maxd"&gt;Max Desiatov&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Building an Accessibility Culture, One Step at a Time - &lt;a href="https://basbroek.nl/"&gt;Bas Broek&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Speeding up protocol conformances in Swift - &lt;a href="https://x.com/sond813"&gt;Noah Martin&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Hidden engineering challenges of A/B Testing - &lt;a href="https://linkedin.com/in/anastasia-ios"&gt;Anastasia Petrova&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Swift Concurrency is new and different and hard and you can do it - &lt;a href="https://massicotte.org/"&gt;Matt Massicotte&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Crafting better App Icons - &lt;a href="https://x.com/flora_dam"&gt;Flora Damiano&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;How LEGO can inspire us to build better UI Libraries - &lt;a href="https://x.com/joseph_mallah"&gt;Joseph El Mallah&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;From Side Project to Going Indie - Antoine van der Lee&lt;/h2&gt;
&lt;p&gt;Key Takeaways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Prepare&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Mindset&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Never quit because it becomes challenging&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;img src="/resources/sl_1_1.png" alt="image" loading="lazy"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Learn by shipping&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Goals &amp;amp; Planning&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Goals visualize progress; progress leads to motivation&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A tool to not do too much: you’ve already reached your goal&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Write all tasks out, it is easier to track.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create issues for everything, even when the task is already completed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The first step is the hardest, break it into smaller steps&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Get into action&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Development Practices&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Creating and Utilizing a Personal SDK for Reusability (Benefit of Isolation and Focus)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Tests will save you time&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Marketing &amp;amp; Audience Growth&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;REPURPOSE YOUR KNOWLEDGE GAINS&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Learn something, Blog about it, Repurpose on social media, Apply to your product&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Only ~2% of your followers will see your post&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use automation to repost evergreen content&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Ensure to at least have 3 posts per day&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Repost for different timezones&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a Personality (Make your personal account likable)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Financial Strategies&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Make sure to know how to measure important metrics&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Monthly Recurring Revenue (MRR)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Annual Recurring Revenue (ARR)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Gross vs. Net&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Define Your Offering&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Build a recurring income with auto-renewing subscription (if it makes sense)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Be careful with lifetime&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Your product grows and evolves; the lifetime price remains the same&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Offer a free experience to add opportunities to upsell&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Reflect&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Sustaining Your Indie Career&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Time management and prioritization becomes even more important&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Everything impacts your income, so it’s easy to overwork&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Planning your days is crucial, you might need to force yourself to take time off&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Always Remember Your Core Values&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;You do what you love&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You’ve got flexible working hours&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;More time with your family&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;More information on &lt;a href="https://going-indie.com"&gt;going-indie.com&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Multi-Platform Libraries with Swift for WebAssembly - Max Desiatov&lt;/h2&gt;
&lt;p&gt;Key Takeaways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;WebAssembly Text Format (.wat extension) used for debugging&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;WebAssembly binary modules (.wasm extension) used for distribution&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;With WASI syscalls you can interact with the host environment (e.g. filesystem, network, this requires explicit permissions)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Wasm is a virtual instruction set designed for security and efficiency&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;WASI defines API and ABI for accessing the host system&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Apps can embed a Wasm runtime for plugins-like functionality&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Resources:&lt;br&gt;
Demo code available on GitHub: &lt;a href="https://github.com/apple/swift-for-wasm-examples"&gt;https://github.com/apple/swift-for-wasm-examples&lt;/a&gt;&lt;br&gt;
WasmKit source repository: &lt;a href="https://github.com/swiftwasm/wasmkit"&gt;https://github.com/swiftwasm/wasmkit&lt;/a&gt;&lt;br&gt;
Join the conversation at "Swift for WebAssembly" Forums section: &lt;a href="https://forums.swift.org/c/related-projects/swift-for-webassembly/71"&gt;https://forums.swift.org/c/related-projects/swift-for-webassembly/71&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Building an Accessibility Culture, One Step at a Time - Bas Broek&lt;/h2&gt;
&lt;p&gt;Key Takeaways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Accessibility is an &lt;em&gt;us&lt;/em&gt; problem.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;What's (not) accessibility?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;☑️ Localisation&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;☑️ Dynamic Type&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;☑️ Autocomplete&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;☑️ Dark mode&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;☑️ Sensory feedback&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Speeding up protocol conformances in Swift - Noah Martin&lt;/h2&gt;
&lt;p&gt;Key Takeaways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Don't use protocola (and conformances) if not needed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;img src="/resources/sl_1_2.png" alt="image" loading="lazy"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;img src="/resources/sl_1_3.png" alt="image" loading="lazy"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For more info see &lt;a href="https://forums.swift.org/t/pitch-speed-up-protocol-conformance-checks-on-first-launch-using-bloom-filters/70225/29"&gt;this topic&lt;/a&gt; on the &lt;a href="https://forums.swift.org"&gt;Swift Forums&lt;/a&gt;, or read more about it on the blog of &lt;a href="https://www.emergetools.com/blog/posts/SwiftProtocolConformance"&gt;Emerge Tools&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Hidden engineering challenges of A/B Testing - Anastasia Petrova&lt;/h2&gt;
&lt;p&gt;Key Takeaways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;A/B testing makes customer support harder and FAQ's unreliable &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Removing A/B testing can be tricky if it is based on (a lot of) &lt;code&gt;if #available(..)&lt;/code&gt; modifiers.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;a good way to update/enroll isn't using willEnterForgeground.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Most experiments fail.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;1 of 8 experiments succeed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A/B testing is an expensive way of saying "I disagree with your idea".&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Swift Concurrency is new and different and hard and you can do it - Matt Massicotte&lt;/h2&gt;
&lt;p&gt;Matt shared his thoughts on Swift Concurrency and how developers can leverage it in their projects.&lt;/p&gt;
&lt;p&gt;Key Takeaways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Switch over to Swift6 concuccency mode if you start a new project.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Do you want to know more about concurrency visit &lt;a href="https://www.massicotte.org/"&gt;Matt's website&lt;/a&gt; or browse &lt;a href="/blog/tag/Concurrency"&gt;Concurrency&lt;/a&gt; tags on this blog.&lt;/p&gt;
&lt;h2&gt;Crafting better App Icons - Flora Damiano&lt;/h2&gt;
&lt;p&gt;Key Takeaways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Make it Versatile&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Ensure the effectiveness of your icon design across various platforms and sizes.&lt;/strong&gt;&lt;br&gt;
The icon will be displayed in multiple locations throughout the platform, emphasizing the importance of maintaining both legibility and uniqueness.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Complex icons that attempt to incorporate too much information often suffer from poor scalability.&lt;/strong&gt;&lt;br&gt;
Prioritize the simplicity of lines and shapes during the conceptual stage and favor ideas that can be conveyed through a singular object.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Avoid the pitfall of attempting to represent diverse functionalities within a single icon, as this can lead to confusion.&lt;/strong&gt;&lt;br&gt;
Using high-contrasted colors and vibrant shades of the same color is a common approach to ensure recognizability even in smaller usages of your design.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Make it Consistent&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Establishing a strong and consistent design language is a potent means of leaving a lasting impression on users.&lt;/strong&gt;&lt;br&gt;
Effective app icon design serves as an extension of the application's essence. Ensuring alignment between the app's purpose and visual representation enhances the overall user experience, fostering memorability.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;A straightforward approach to achieving this synergy is incorporating dominant colors from the app or branding into the icon.&lt;/strong&gt;&lt;br&gt;
providing a foundational and consistent element. Additionally, consider transforming existing graphic elements into iconography, further reinforcing the cohesive design language. By maintaining consistency in these visual aspects, you enhance the overall effectiveness and impact of your app icon.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;When you have a multiplatform app or a bundle with multiple apps, consistency will play a big role in how effective the App Icon design is.&lt;/strong&gt;&lt;br&gt;
Keeping the same visual language also increases recognizability through platforms and markets. This is crucial also when changes to your icons are applied over time.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Make it Original&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Make it Memorable&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;There are some key attributes that you have to make sure that your file matches:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Always provide a squared image as an App Icon, don’t apply a corner radius. The system will automatically apply a mask for the platform you are designing for.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Work in sRBG or P3 color space. Xcode runs in P3 color space and sRBG almost matches this spectrum, but working in different spaces will result in an inconsistency in colors applied. Since you are working on display only, remember to work on the resolution of 72 dpis.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Don’t apply transparency in your background layer. Your app icon will be displayed in different screen scenarios, and having transparency on the background will result in color changes and inconsistency, plus color readability and Xcode errors.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;PNG file format works best with Xcode and raster quality.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To ensure consistency with the system, you will need to provide 3 different variants of your App Icon: Light Mode, Dark Mode, and Tinted&lt;/p&gt;
&lt;p&gt;Resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://www.createwithswift.com/crafting-better-app-icons/"&gt;https://www.createwithswift.com/crafting-better-app-icons/&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://www.createwithswift.com/preparing-your-app-icon-for-dark-and-tinted-appearance/"&gt;https://www.createwithswift.com/preparing-your-app-icon-for-dark-and-tinted-appearance/&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;How LEGO can inspire us to build better UI Libraries - Joseph El Mallah&lt;/h2&gt;
&lt;p&gt;Key Takeaways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Create Composable Views instead of a view for each element you want to show.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Composable makes it easier to&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Change the requirements&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Localize&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Layout direction&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Accessibility&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Test&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/videos/play/wwdc2024/10146/"&gt;https://developer.apple.com/videos/play/wwdc2024/10146/&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;That's a wrap for day 1 of SwiftLeeds 2024!&lt;/p&gt;</description>
  </item>
  <item>
    <title>SwiftLeeds 2024 (day 2)</title>    <link>https://wesleydegroot.nl/blog/swiftleeds-2024-2</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/swiftleeds-2024-2</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:04 +0200</pubDate>
    <category>SwiftLeeds</category>
    <category>Conference</category>
    <description>&lt;p&gt;It's that time of year again!&lt;br&gt;
SwiftLeeds 2024 is here, and in this post, i'll be sharing my thoughts on the second (and last) day of the conference.&lt;/p&gt;
&lt;h4&gt;Agenda:&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The Roast of your App's Design - &lt;a href="https://mastodon.design/@hidde"&gt;Hidde van der Ploeg&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;From Quantum to Code: A Teacher's Journey into Mobile Development - &lt;a href="https://linkedin.com/in/chloe-jenner-b1a8b9231"&gt;Chloe Jenner&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Cut costs, not corners - Master modularization with SPM - &lt;a href="https://linkedin.com/in/antonio-markotic"&gt;Antonio Markotić&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The Art of Functional State Management - &lt;a href="https://x.com/araksavoyan"&gt;Araks Avoyan&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fixing Image Performance for The Worst iPhone - &lt;a href="https://x.com/avielgr"&gt;Aviel Gross&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Scalable Continuous Integration for iOS - &lt;a href="https://x.com/albertodebo"&gt;Alberto De Bortoli&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;What Not to Do: Rocky Road to Banner View - &lt;a href="https://x.com/mouse_or_cleg"&gt;Maria Kharybina&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Refactoring Fear - &lt;a href="https://mastodon.social/@joshdholtz"&gt;Josh Holtz&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The Roast of your App's Design - Hidde van der Ploeg&lt;/h2&gt;
&lt;p&gt;Hidde shared his insights on how to improve your app's design by roasting it.&lt;/p&gt;
&lt;p&gt;Key Takeaways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Make sure your app is accessible to everyone.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Make sure your app is usable by everyone.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Don't make your app too complex, it should be easy to navigate in.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you don’t know typography, don’t do custom fonts.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Make use of the system colors and their options (&lt;code&gt;.secondary&lt;/code&gt;, &lt;code&gt;.tertiary&lt;/code&gt;, &lt;code&gt;.quaternary&lt;/code&gt;, &lt;code&gt;.quinary&lt;/code&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use &lt;a href="/blog/ContentUnavailableView"&gt;&lt;code&gt;ContentUnavailableView&lt;/code&gt;&lt;/a&gt; when there is no content to show.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When apps have great navigation, it's often unnoticed because people can focus on the content and experience.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Look at the &lt;a href="https://developer.apple.com/design/human-interface-guidelines"&gt;Human Interface Guidelines&lt;/a&gt; for inspiration and guidance.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/videos/play/wwdc2022/10001"&gt;Explore navigation design in iOS&lt;/a&gt; and &lt;a href="https://developer.apple.com/design/human-interface-guidelines/ios/app-architecture/navigation/"&gt;HIG: Navigation&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;From Quantum to Code: A Teacher's Journey into Mobile Development - Chloe Jenner&lt;/h2&gt;
&lt;p&gt;Chloe discussed her transition from quantum physics to mobile development in a poetic and inspiring talk.&lt;br&gt;
This talk was a great reminder that it's never too late to change careers and pursue your passion.&lt;br&gt;
View the talk on &lt;a href="https://www.youtube.com/watch?v=k17YKrhPU4o"&gt;YouTube&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Cut costs, not corners - Master modularization with SPM - Antonio Markotić&lt;/h2&gt;
&lt;p&gt;Antonio talked about the benefits of modularization with Swift Package Manager.&lt;/p&gt;
&lt;p&gt;Key Takeaways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Modularization can help you reduce build times and dependencies.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Modularization can help you cut costs on CI/CD.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Modularized iOS app&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;tr&gt;&lt;th&gt;Pros&lt;/th&gt;&lt;th&gt;Cons&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Faster build times&lt;/td&gt;&lt;td&gt; Initial setup can be time-consuming&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Loosley coupled code&lt;/td&gt;&lt;td&gt; Module inconsistency&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Adding new feature&lt;/td&gt;&lt;td&gt; Possible circular references&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Reusability&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Testobility&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Parallel development&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;h2&gt;The Art of Functional State Management - Araks Avoyan&lt;/h2&gt;
&lt;p&gt;Araks shared her expertise on functional state management in iOS apps.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key features of Functional programming&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Immutability.&lt;br&gt;
we don't directly change our data. We create a new copy of the data with the changes we want to make. This makes our code more predictable and easier to reason about.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Combine.&lt;br&gt;
Combine is Apple's framework for reactive programming. It allows us to work with asynchronous data streams in a declarative way.&lt;br&gt;
&lt;em&gt;Cons of combine:&lt;/em&gt; unpredictable future from apple (async, await, Swift 6).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Fixing Image Performance for The Worst iPhone - Aviel Gross&lt;/h2&gt;
&lt;p&gt;Aviel discussed strategies for improving image performance on older iPhone models.&lt;/p&gt;
&lt;p&gt;Key Takeaways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;It can be tricky to load a large number of images in a performant way on older devices.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Sometimes it is better to use a framework like Nuke or Kingfisher instead of relying on SwiftUI's built-in image loading.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It can be smart to (pre)load blurred images first and then replace them with the full resolution image.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Scalable Continuous Integration for iOS - Alberto De Bortoli&lt;/h2&gt;
&lt;p&gt;Alberto shared his insights on building scalable continuous integration pipelines for iOS projects.&lt;br&gt;
Key Takeaways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;There is no one-size-fits-all solution for continuous integration.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You can also host your own CI server on a Mac (mini).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Using Terraform you can easily deploy more CI's when needed on AWS.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;What Not to Do: Rocky Road to Banner View - Maria Kharybina&lt;/h2&gt;
&lt;p&gt;Maria discussed the challenges she faced while implementing a banner view in an iOS app.&lt;/p&gt;
&lt;p&gt;Key Takeaways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;What NOT to do:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;ignore advantages of chosen framework.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;start looking for solution before defining a problem&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;only solve the current problem&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;change the requirements for the component as you go&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;forget about testing&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;think before coding&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;spend more time on assessment and planning&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;share your wins and &lt;u&gt;fails&lt;/u&gt; with the community&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Refactoring Fear - Josh Holtz&lt;/h2&gt;
&lt;p&gt;Josh shared his experiences with his stutter and how it has affected his life and career.&lt;br&gt;
This talk was a great reminder that everyone has their own struggles and that it's important to be kind to yourself and others.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;That's a wrap for day 2 of SwiftLeeds 2024!&lt;/p&gt;</description>
  </item>
  <item>
    <title>Understanding Package.swift</title>    <link>https://wesleydegroot.nl/blog/understanding-swift-package</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/understanding-swift-package</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:05 +0200</pubDate>
    <category>SwiftPM</category>
    <category>Package.swift</category>
    <description>&lt;p&gt;If you're diving into Swift development, you've likely encountered &lt;code&gt;Package.swift&lt;/code&gt;.&lt;br&gt;
This file is the cornerstone of Swift Package Manager (SPM), Apple's tool for managing Swift code dependencies.&lt;br&gt;
Let's explore what makes &lt;code&gt;Package.swift&lt;/code&gt; so essential and how you can leverage it in your projects.&lt;/p&gt;
&lt;h2&gt;What is Package.swift?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Package.swift&lt;/code&gt; is a manifest file that defines the structure and dependencies of a Swift package.&lt;br&gt;
It uses Swift syntax to describe the package's configuration, making it both powerful and easy to read.&lt;br&gt;
This file is crucial for managing dependencies, building libraries, and sharing code across different projects.&lt;/p&gt;
&lt;h3&gt;Key Components of Package.swift/Table of Contents&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="#PackageDescription"&gt;Package Description&lt;/a&gt;&lt;/strong&gt;: The top-level structure that includes metadata about the package, such as its name, platforms, and Swift tools version.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="#Platforms"&gt;Platforms&lt;/a&gt;&lt;/strong&gt; (Optional): Defines the platforms which your package supports.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="#Products"&gt;Products&lt;/a&gt;&lt;/strong&gt;: Defines the executables and libraries produced by the package. These can be used by other packages or applications.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="#Dependencies"&gt;Dependencies&lt;/a&gt;&lt;/strong&gt;: Lists external packages that your package depends on. SPM will fetch and manage these dependencies for you.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="#Targets"&gt;Targets&lt;/a&gt;&lt;/strong&gt;: The basic building blocks of a package. Each target can define a module or a test suite.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="#Resources"&gt;Resources&lt;/a&gt; (Optional)&lt;/strong&gt;: Bundles resources with the package, such as images or sound files.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="#AdvancedTopics"&gt;Advanced Topics&lt;/a&gt;&lt;/strong&gt;: [Not required] Version-specific &lt;code&gt;Package.swift&lt;/code&gt; files, publishing your Swift package, and more.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Why Use Swift Package Manager?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Simplified Dependency Management&lt;/strong&gt;: SPM handles the downloading and linking of dependencies, ensuring compatibility and reducing conflicts.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Integration with Xcode&lt;/strong&gt;: Xcode has built-in support for SPM, making it easy to add and manage packages directly within your project.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Cross-Platform Support&lt;/strong&gt;: SPM supports macOS, iOS, watchOS, and tvOS, allowing you to share code across different platforms.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Creating your first Package (Using the terminal)&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Creating a New Package&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;mkdir MyPackage
cd MyPackage
swift package init&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command creates a new package with a default &lt;code&gt;Package.swift&lt;/code&gt; file.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Swift Generates necessary files&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Creating library package: MyPackage
Creating Package.swift
Creating .gitignore
Creating Sources/
Creating Sources/MyPackage/MyPackage.swift
Creating Tests/
Creating Tests/MyPackage/
Creating Tests/MyPackageTests/MyPackageTests.swift&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;A look into Package.swift&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;// swift-tools-version:5.6
import PackageDescription

let package = Package(
    name: "MyPackage",
    products: [
        .library(
            name: "MyPackage",
            targets: ["MyPackage"]),
    ],
    dependencies: [
        // Dependencies for package (if any)
    ],
    targets: [
        .target(
            name: "MyPackage",
            dependencies: []),
        .testTarget(
            name: "MyPackageTests",
            dependencies: ["MyPackage"]),
    ]
)&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Building and Testing&lt;/strong&gt;:&lt;br&gt;
Use the following commands to build and test your package:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;swift build
swift test&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Naming Conventions&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;// swift-tools-version:5.6
import PackageDescription

let package = Package(
    name: "MyPackage",
    products: [
        .library(
            name: "MyPackage", // This is the name which will be used in the import statement
            targets: ["MyPackage"]),
    ]
)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What does Apple do: Apple hosts their packages, in most cases, such that the last url component and the package name match. These identifiers are formed using kebab-case, and start with the swift- prefix. See the results of this query for all their packages that fit this format. Looking at swift-async-algorithms, one sees that the last path component matches the the package name, while the product name is different: SwiftAlgorithms&lt;/p&gt;
&lt;p&gt;&lt;a name="Platforms"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Platforms&lt;/h2&gt;
&lt;p&gt;You can also define on which platforms your package works, this makes it easier for the users of your package to see if your package is compatible with the platform they are developing on.&lt;br&gt;
The supported platforms types (2024) are: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/packagedescription/supportedplatform/ios(_:)-5pvv5"&gt;.ios()&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/packagedescription/supportedplatform/macos(_:)-2wthp"&gt;.macOS()&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/packagedescription/supportedplatform/watchos(_:)-t998"&gt;.watchOS()&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/packagedescription/supportedplatform/visionos(_:)-3ip0z"&gt;.visionOS()&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/packagedescription/supportedplatform/tvos(_:)-6931l"&gt;.tvOS()&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/packagedescription/supportedplatform/maccatalyst(_:)-6bh40"&gt;.macCatalyst()&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/packagedescription/supportedplatform/driverkit(_:)-jxlz"&gt;.driverKit()&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/packagedescription/platform/linux"&gt;.linux&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/packagedescription/platform/wasi"&gt;.wasi&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/packagedescription/platform/openbsd"&gt;.openbsd&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/packagedescription/supportedplatform/custom(_:versionstring:)"&gt;.custom()&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    platforms: [
        .macOS(.v10_15),
        .iOS(.v13)
    ]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a name="Products"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Products&lt;/h2&gt;
&lt;p&gt;Products are the executables and libraries produced by the package.&lt;br&gt;
You can define the products in the &lt;code&gt;Package.swift&lt;/code&gt; file as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;    products: [
        .library(
            name: "MyPackage",
            targets: ["MyPackage"]
        )
    ]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a name="Dependencies"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Dependencies&lt;/h2&gt;
&lt;p&gt;Sometimes you want to use a package from someone else to help you with your project (e.g. &lt;a href="https://github.com/0xWDG/SimpleNetworking"&gt;SimpleNetworking&lt;/a&gt;) to make our network calls easier.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You can use a specific version of a package by specifying the version in the &lt;code&gt;Package.swift&lt;/code&gt; file:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;    dependencies: [
        .package(
            url: "https://github.com/0xWDG/SimpleNetworking.git",
            from: "1.0.0"
        )
    ]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;You can use a specific branch of a package by specifying the branch in the &lt;code&gt;Package.swift&lt;/code&gt; file:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;    dependencies: [
        .package(
            url: "https://github.com/0xWDG/SimpleNetworking.git", 
            branch: "main"
        )
    ]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;You can use a specific commit of a package by specifying the commit in the &lt;code&gt;Package.swift&lt;/code&gt; file:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;    dependencies: [
        .package(
            url: "https://github.com/0xWDG/SimpleNetworking.git",
            .revision("68726dd")
        )
    ]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a name="Targets"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Targets&lt;/h2&gt;
&lt;p&gt;Targets are the basic building blocks of a package, defining a module or a test suite.&lt;br&gt;
Targets can depend on other targets in this package and products from dependencies.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;    targets: [
        // Targets are the basic building blocks of a package, defining a module or a test suite.
        // Targets can depend on other targets in this package and products from dependencies.
        .target(
            name: "MyPackage"
        ),
        .testTarget(
            name: "MyPackageTests",
            dependencies: ["MyPackage"]
        )
    ]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a name="Resources"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Resources&lt;/h2&gt;
&lt;h3&gt;Bundling resources with a Swift Package&lt;/h3&gt;
&lt;p&gt;Swift Packages can contain resources that are bundled with the package.&lt;br&gt;
Resources can include images, sounds, or any other files that your package needs to function correctly.&lt;/p&gt;
&lt;h4&gt;Adding resources to a Swift Package&lt;/h4&gt;
&lt;p&gt;You can add resources by updating your target definition:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Process all resources found in the Resources directory:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;.target(
    name: "MyPackage",
    resources: [
        .process("Resources/")
    ]
)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Only add a specific file:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;.target(
    name: "MyPackage",
    resources: [
        .process("Resources/image.png")
    ]
)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Copy all resources found in the Resources directory:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;.target(
    name: "MyPackage",
    resources: [
        .copy("Resources/")
    ]
)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Copy a specific file:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;.target(
    name: "MyPackage",
    resources: [
        .copy("Resources/image.png")
    ]
)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As demonstrated in the code example, there are several ways of adding resources. For most use cases, using the process rule will be sufficient. It’s essential to realize Xcode might optimize your files.&lt;br&gt;
For example, it might optimize images for a specific platform. If using the original files is necessary, consider using the copy rule.&lt;/p&gt;
&lt;p&gt;Excluding specific resources&lt;br&gt;
If needed, you can exclude specific resources using the exclude definition:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;.target(
    name: "MyPackage",
    exclude: ["Readme.md"],
    resources: [
        .process("Resources/")
    ]
)&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Accessing resources in code using the module bundle&lt;/h3&gt;
&lt;p&gt;You can access any resources using the Bundle.module accessor.&lt;br&gt;
Note that the module property will only become available if there are any resources rules defined in the package target.&lt;br&gt;
It’s important to note that the following code won’t work for SwiftUI in packages:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;var body: some View {
    Image("sample_image", bundle: .module)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead, you’ll have to rely on UIKit/Appkit and load the image as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ContentView: View {

    var image: UIImage {
        return UIImage(named: "sample_image", in: .module, compatibleWith: nil)!
    }

    var body: some View {
        Image(uiImage: image)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is unfortunate, you can also use this extension to load images in SwiftUI:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

extension Image {
    init(packageResource name: String, ofType type: String) {
        #if canImport(UIKit)
        guard let path = Bundle.module.path(forResource: name, ofType: type),
              let image = UIImage(contentsOfFile: path) else {
            self.init(name)
            return
        }
        self.init(uiImage: image)
        #elseif canImport(AppKit)
        guard let path = Bundle.module.path(forResource: name, ofType: type),
              let image = NSImage(contentsOfFile: path) else {
            self.init(name)
            return
        }
        self.init(nsImage: image)
        #else
        self.init(name)
        #endif
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Source: &lt;a href="https://github.com/eneko/Blog/issues/28"&gt;Eneko Alonso&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can then use the extension as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;var body: some View {
    Image(packageResource: "sample_image", ofType: "png")
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For any other resources, you can rely on accessing resources directly using the Bundle:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;Bundle.module.url(forResource: "sample_text_resource", withExtension: "txt")&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a name="AdvancedTopics"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Advanced Topics&lt;/h2&gt;
&lt;h2&gt;Version-specific Package.swift Files&lt;/h2&gt;
&lt;h4&gt;Benefits of Version-specific Package.swift Files&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Backward Compatibility&lt;/strong&gt;: Maintain support for older Swift versions while adopting new features in newer versions.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Granular Control&lt;/strong&gt;: Tailor your package configuration to specific Swift versions, ensuring optimal performance and compatibility.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Future-proofing&lt;/strong&gt;: Prepare your packages for upcoming Swift releases without disrupting existing users.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;Create a version-specific &lt;code&gt;Package.swift&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;To create a version-specific &lt;code&gt;Package.swift&lt;/code&gt; file, you simply rename the file to include the Swift version it targets.&lt;br&gt;
The format is &lt;code&gt;Package@swift-&amp;lt;MAJOR&amp;gt;.&amp;lt;MINOR&amp;gt;.&amp;lt;PATCH&amp;gt;.swift&lt;/code&gt;. Here are some examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Package@swift-5.7.swift&lt;/code&gt;: Applies to all patch versions of Swift 5.7.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Package@swift-5.7.1.swift&lt;/code&gt;: Applies exclusively to Swift 5.7.1.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Package@swift-5.swift&lt;/code&gt;: Applies to all minor and patch versions of Swift 5.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Example Structure&lt;/h4&gt;
&lt;p&gt;Let's say you want to support Swift 5.6 and Swift 5.7 with different configurations. You would create two files:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;Package@swift-5.6.swift&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;// swift-tools-version:5.6
import PackageDescription

let package = Package(
    name: "MyPackage",
    platforms: [
        .macOS(.v10_15),
        .iOS(.v13)
    ],
    products: [
        .library(
            name: "MyPackage",
            targets: ["MyPackage"]),
    ],
    dependencies: [
        // Dependencies for Swift 5.6
    ],
    targets: [
        .target(
            name: "MyPackage",
            dependencies: []),
        .testTarget(
            name: "MyPackageTests",
            dependencies: ["MyPackage"]),
    ]
)&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;Package@swift-5.7.swift&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;// swift-tools-version:5.7
import PackageDescription

let package = Package(
    name: "MyPackage",
    platforms: [
        .macOS(.v11),
        .iOS(.v14)
    ],
    products: [
        .library(
            name: "MyPackage",
            targets: ["MyPackage"]),
    ],
    dependencies: [
        // Dependencies for Swift 5.7
    ],
    targets: [
        .target(
            name: "MyPackage",
            dependencies: []),
        .testTarget(
            name: "MyPackageTests",
            dependencies: ["MyPackage"]),
    ]
)&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Publishing your Swift Package&lt;/h2&gt;
&lt;p&gt;To publish your Swift package you can simply create a new tag on your Git repository.&lt;br&gt;
As you’ve seen in the dependencies section, you can add references to dependencies using Git URLs.&lt;br&gt;
To enable developers to explore packages more easily, &lt;a href="https://mastodon.social/@daveverwer"&gt;Dave Verwer&lt;/a&gt; and &lt;a href="https://mastodon.social/@finestructure"&gt;Sven A. Schmidt&lt;/a&gt; have founded the &lt;a href="https://swiftpackageindex.com/"&gt;Swift Package Index&lt;/a&gt;. You can start &lt;a href="https://github.com/SwiftPackageIndex/PackageList/issues/new?assignees=&amp;amp;labels=Add+Package&amp;amp;projects=&amp;amp;template=add_package.yml&amp;amp;title=Add+%3CPackage%3E"&gt;adding your package(s)&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Wrap-up&lt;/h2&gt;
&lt;p&gt;Swift Packages are super cool and can help you manage your dependencies in a more structured way.&lt;br&gt;
Setting up a Swift Package for the first time can be a bit overwhelming, but once you get the hang of it, it's a breeze.&lt;br&gt;
i hope this article helped you understand the basics of &lt;code&gt;Package.swift&lt;/code&gt; and how you can leverage it in your projects.&lt;br&gt;
if you have any questions or feedback, feel free to reach out to me on &lt;a href="https://mastodon.social/@0xWDG"&gt;Mastodon&lt;/a&gt;, &lt;a href="https://twitter.com/0xWDG"&gt;Twitter&lt;/a&gt;, or comment down below.&lt;/p&gt;
&lt;h4&gt;References&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://swift.org/package-manager/"&gt;Swift Package Manager Documentation&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://swiftpackageindex.com/"&gt;Swift Package Index&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Translation framework in Swift</title>    <link>https://wesleydegroot.nl/blog/translation-framework-in-swift</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/translation-framework-in-swift</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:05 +0200</pubDate>
    <category>Swift</category>
    <category>Translation</category>
    <category>Framework</category>
    <description>&lt;h3&gt;Exploring the Translation Framework in Swift&lt;/h3&gt;
&lt;p&gt;With the release of iOS 17.4, Apple introduced the &lt;strong&gt;Translation framework&lt;/strong&gt;, a powerful tool that allows developers to integrate text translation capabilities directly into their Swift applications. This framework leverages CoreML models to perform on-device translations, ensuring fast and secure translations without the need for an internet connection.&lt;/p&gt;
&lt;h3&gt;Key Features&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;On-Device Translation&lt;/strong&gt;: Unlike third-party services that require an internet connection, the Translation framework performs translations on-device, enhancing speed and security.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Built-in UI&lt;/strong&gt;: The framework offers a built-in user interface for translations, making it easy to integrate without extensive customization.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Customizable Experience&lt;/strong&gt;: For developers who need more control, the framework provides APIs to customize the translation process.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Getting Started&lt;/h3&gt;
&lt;p&gt;To demonstrate how to use the Translation framework, let's build a simple SwiftUI app that translates text input by the user.&lt;/p&gt;
&lt;h2&gt;Using the Built-in User Interface&lt;/h2&gt;
&lt;p&gt;We can achive this by using the &lt;a href="https://developer.apple.com/documentation/swiftui/view/translationpresentation(ispresented:text:attachmentanchor:arrowedge:replacementaction:)"&gt;.translationPresentation(isPresented:text:)&lt;/a&gt; view modifier provided by the translation framework.&lt;br&gt;
This modifier presents a built-in translation UI that allows users to input text and view the translated output. it also provides a callback to handle the translated text if needed.&lt;/p&gt;
&lt;h3&gt;There are two ways to use the built-in UI:&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Only show the translation&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.translationPresentation(isPresented: $showTranslation, text: text)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Handle the translated text&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.translationPresentation(isPresented: $showTranslation, text: text) { translatedText in
    text = translatedText
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Example code&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI
import Translation

struct ContentView: View {
    /// Text to be translated
    @State
    var text = "Swift programming is fun! You should try it yourself, it even has a translation framework now!"

    /// Should we show the translation UI
    @State
    var showTranslation = false

    var body: some View {
        NavigationStack {
            VStack {
                // Display the text to be translated
                Text(text)
                    .font(.title3)
                    .multilineTextAlignment(.center)
            }

            // Show translation UI
            .translationPresentation(
                isPresented: $showTranslation, 
                text: text
            ) { translatedText in
                // Optional: Handle the translated text
                text = translatedText
            }

            // Add a button to show the translation UI
            .toolbar {
                Button {
                    // Toggle the translation UI
                    showTranslation.toggle()
                } label: {
                    Image(systemName: "translate")
                }
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Using your own User Interface&lt;/h2&gt;
&lt;p&gt;The Translation framework also provides APIs to create a custom translation experience. By using the  view modifier, you can initiate translation tasks and handle the translated text within your app's UI.&lt;/p&gt;
&lt;h3&gt;Inline translation using the &lt;a href="https://developer.apple.com/documentation/swiftui/view/translationtask(_:action:)"&gt;&lt;code&gt;.translationTask(_:action:)&lt;/code&gt;&lt;/a&gt; view modifier (user initiated translation)&lt;/h3&gt;
&lt;p&gt;The &lt;a href="https://developer.apple.com/documentation/swiftui/view/translationtask(_:action:)"&gt;&lt;code&gt;.translationTask(_:action:)&lt;/code&gt;&lt;/a&gt; view modifier allows you to create a custom translation experience where users can initiate translation tasks by interacting with your app's UI.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct ContentView: View {
    /// Source text for translation
    @State
    private var sourceText = "Swift programming is fun! You should try it yourself, it even has a translation framework now!"

    /// Source text for translation
    var sourceLanguage: Locale.Language?

    /// Target language for translation
    var targetLanguage: Locale.Language?

    /// Translated text
    @State
    private var targetText: String?

    /// Translation session configuration
    @State
    private var configuration: TranslationSession.Configuration?

    var body: some View {
        VStack {
            Text(targetText ?? sourceText)
            Button("Translate") {
                guard configuration == nil else {
                    configuration?.invalidate()
                    return
                }

                // Create a new translation session configuration
                configuration = TranslationSession.Configuration(
                    source: sourceLanguage,
                    target: targetLanguage
                )
            }
            // Start translation task
            .translationTask(configuration) { session in
                Task { @MainActor in
                    do {
                        // Perform translation
                        let response = try await session.translate(sourceText)

                        // Update target text
                        targetText = response.targetText
                    } catch {
                        // code to handle error
                    }
                }
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Inline translation using the &lt;a href="https://developer.apple.com/documentation/swiftui/view/translationtask(_:action:)"&gt;&lt;code&gt;.translationTask(source:target:action:)&lt;/code&gt;&lt;/a&gt; view modifier (automatic translation)&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/translationtask(_:action:)"&gt;&lt;code&gt;.translationTask(source:target:action:)&lt;/code&gt;&lt;/a&gt; view modifier allows you to create a custom translation experience where users do not need to initiate translation tasks.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI
import Translation

struct ContentView: View {
    /// Source text for translation
    @State
    private var sourceText = "Swift programming is fun! You should try it yourself, it even has a translation framework now!"

    /// Source language for translation
    var sourceLanguage: Locale.Language?

    /// Target language for translation
    var targetLanguage: Locale.Language?

    /// Translated text
    @State
    private var targetText: String?

    var body: some View {
        Text(targetText ?? sourceText)
            // Start translation task
            .translationTask(
                source: sourceLanguage,
                target: targetLanguage
            ) { session in
                Task { @MainActor in
                    do {
                        // Perform translation
                        let response = try await session.translate(sourceText)

                        // Update target text
                        targetText = response.targetText
                    } catch {
                        // code to handle error
                    }
                }
            }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;The Translation framework in Swift opens up new possibilities for creating multilingual applications with ease. By leveraging on-device translations, developers can provide a seamless and secure user experience. Whether you use the built-in UI or customize the translation process, this framework is a valuable addition to your development toolkit.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/Translation"&gt;https://developer.apple.com/documentation/Translation&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/translationpresentation(ispresented:text:attachmentanchor:arrowedge:replacementaction"&gt;https://developer.apple.com/documentation/swiftui/view/translationpresentation(ispresented:text:attachmentanchor:arrowedge:replacementaction&lt;/a&gt;:)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/translationtask(_:action"&gt;https://developer.apple.com/documentation/swiftui/view/translationtask(_:action&lt;/a&gt;:)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/translationtask(source:target:action"&gt;https://developer.apple.com/documentation/swiftui/view/translationtask(source:target:action&lt;/a&gt;:)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Building an Asynchronous Button in SwiftUI</title>    <link>https://wesleydegroot.nl/blog/async-buttons-in-swiftui</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/async-buttons-in-swiftui</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:06 +0200</pubDate>
    <category>SwiftUI</category>
    <category>Asynchronus</category>
    <description>&lt;p&gt;Sometimes, we need to perform asynchronous tasks when a button is tapped in a SwiftUI application. For example, fetching data from a network request, saving data to a database, or performing any long-running operation. In this tutorial, we'll explore how to create an asynchronous button in SwiftUI that performs an asynchronous task when tapped.&lt;/p&gt;
&lt;h4&gt;Why Use Asynchronous Buttons?&lt;/h4&gt;
&lt;p&gt;Asynchronous tasks are essential for operations that take time to complete, such as network requests, file I/O, or any long-running computations. By using asynchronous buttons, we can keep the UI responsive and provide feedback to users while the task is being performed.&lt;/p&gt;
&lt;h2&gt;Creating an Async Button&lt;/h2&gt;
&lt;p&gt;In this code snippet, we'll create an &lt;code&gt;AsyncButton&lt;/code&gt; view that performs an asynchronous task when tapped. The button will display a loading indicator while the task is in progress and disable user interaction to prevent multiple taps.&lt;br&gt;
This is a simple example of how to create an asynchronous button in SwiftUI:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

/// A button that performs an asynchronous task when tapped.
struct AsyncButton&amp;lt;Label: View&amp;gt;: View {
    /// The asynchronous action to perform when the button is tapped.
    var action: () async -&amp;gt; Void

    /// The label of the button.
    @ViewBuilder
    var label: () -&amp;gt; Label

    /// Whether the task is currently being performed.
    @State
    private var isPerformingTask = false

    var body: some View {
        Button(action: {
            // When the button is tapped, we are performing a task
            isPerformingTask = true

            // Perform the task asynchronously
            Task {
                // Perform the asynchronous task
                await action()

                // After the task is completed, we are no longer performing a task
                isPerformingTask = false
            }
        }) {
            HStack {
                // Show a loading indicator while the task is in progress
                if isPerformingTask {
                    ProgressView()
                        .controlSize(.mini)
                }

                // Show the label of the button
                label()
            }
        }
        // Disable the button while the task is in progress
        .disabled(isPerformingTask)
    }
}

/// Example usage:
struct ContentView: View {
    var body: some View {
        AsyncButton(action: fetchData) {
            Text("Fetch Data")
        }
    }

    func fetchData() async { // &amp;lt;2&amp;gt;
        // Simulate a network request
        try? await Task.sleep(nanoseconds: 2 * 1_000_000_000)
        print("Data fetched")
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Improving the Async Button&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;AsyncButton&lt;/code&gt; view can be further customized to handle errors and provide feedback to the user. For example, we can display an error message if the task fails or show a success message when the task completes successfully. We can also add animations to enhance the user experience.&lt;/p&gt;
&lt;p&gt;Here's an improved version of the &lt;code&gt;AsyncButton&lt;/code&gt; view that handles errors and provides feedback to the user:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

/// A button that performs an asynchronous task when tapped.
struct AsyncButton&amp;lt;Label: View&amp;gt;: View {
    /// The asynchronous action to perform when the button is tapped.
    var action: () async throws -&amp;gt; Void

    /// The label of the button.
    @ViewBuilder
    var label: () -&amp;gt; Label

    /// Whether the task is currently being performed.
    @State
    private var isPerformingTask = false

    /// The error message to display if the task fails.
    @State
    private var errorMessage: String?

    /// Whether to show the alert.
    @State
    private var showAlert = false

    var body: some View {
        Button(action: {
            // When the button is tapped, we are performing a task
            isPerformingTask = true

            // Perform the task asynchronously
            Task {
                do {
                    // Perform the asynchronous task
                    try await action()

                    // If the task completes successfully, clear the error message (if any)
                    errorMessage = nil
                } catch {
                    // If the task fails, display the error message
                    errorMessage = error.localizedDescription
                }

                // After the task is completed, we are no longer performing a task
                isPerformingTask = false

                // Show the alert if there is an error
                showAlert = errorMessage != nil
            }
        }) {
            HStack {
                // Show a loading indicator while the task is in progress
                if isPerformingTask {
                    // Use a mini progress view
                    ProgressView()
                        .controlSize(.mini)
                }

                // Show the button label
                label()
            }
        }
        // Disable the button while the task is in progress
        .disabled(isPerformingTask)
        // Show an alert with the error message if the task fails
        .alert(isPresented: $showAlert) {
            // Display an alert with the error message
            Alert(
                title: Text("Error"), 
                message: Text(errorMessage ?? ""), 
                dismissButton: .default(Text("OK"))
            )
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Creating an asynchronous button in SwiftUI is straightforward and enhances the user experience by keeping the UI responsive. By leveraging Swift's concurrency model, we can perform tasks asynchronously and handle errors gracefully. This approach ensures that our applications remain smooth and user-friendly, even when performing complex operations.&lt;/p&gt;
&lt;h3&gt;Resources&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/progressview"&gt;https://developer.apple.com/documentation/swiftui/view/progressview&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swift/async"&gt;https://developer.apple.com/documentation/swift/async&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swift/async/await"&gt;https://developer.apple.com/documentation/swift/async/await&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swift/async/task"&gt;https://developer.apple.com/documentation/swift/async/task&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swift/async/throws"&gt;https://developer.apple.com/documentation/swift/async/throws&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Exploring the .inspector Modifier in SwiftUI</title>    <link>https://wesleydegroot.nl/blog/the-inspector-modifier-in-swiftui</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/the-inspector-modifier-in-swiftui</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:06 +0200</pubDate>
    <category>Inspector</category>
    <category>SwiftUI</category>
    <description>&lt;p&gt;SwiftUI continues to evolve, bringing new and powerful tools to developers.&lt;br&gt;
One such tool is the &lt;code&gt;.inspector&lt;/code&gt; modifier, introduced to enhance the user interface by providing context-dependent presentations.&lt;br&gt;
This article will delve into what the &lt;code&gt;.inspector&lt;/code&gt; modifier is, how it works, and how you can use it to create more dynamic and interactive SwiftUI applications.&lt;/p&gt;
&lt;h4&gt;What is the &lt;code&gt;.inspector&lt;/code&gt; Modifier?&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;.inspector&lt;/code&gt; modifier in SwiftUI allows you to present additional information or controls related to a view in a context-dependent manner.&lt;br&gt;
This means that the inspector can appear differently based on the environment it is used in.&lt;br&gt;
For instance, in a compact environment, it might appear as a sheet, while in a regular environment, it could display as a trailing column in your view hierarchy.&lt;/p&gt;
&lt;h4&gt;How to Use the &lt;code&gt;.inspector&lt;/code&gt; Modifier&lt;/h4&gt;
&lt;p&gt;Using the &lt;code&gt;.inspector&lt;/code&gt; modifier is straightforward and similar to other presentation modifiers in SwiftUI, such as &lt;code&gt;.sheet&lt;/code&gt;. Here’s a basic example to illustrate its usage:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct ContentView: View {
    @State
    private var showInspector = false

    var body: some View {
        VStack {
            Button("Show Inspector") {
                showInspector.toggle()
            }
        }
        .inspector(isPresented: $showInspector) {
            Text("Inspector Content")
                .padding()
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, a button toggles the visibility of the inspector. When the button is pressed, the inspector slides in from the trailing edge of the UI, displaying the specified content.&lt;/p&gt;
&lt;h4&gt;Customizing the Inspector&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;.inspector&lt;/code&gt; modifier is highly customizable.&lt;br&gt;
You can add any SwiftUI view inside the inspector, making it a versatile tool for various use cases.&lt;br&gt;
Here’s an example with more complex content:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct ContentView: View {
    @State
    private var showInspector = false

    var body: some View {
        VStack {
            Button("Show Inspector") {
                showInspector.toggle()
            }
        }
        .inspector(isPresented: $showInspector) {
            VStack {
                Text("Inspector Title")
                    .font(.headline)
                Divider()
                Text("Detailed information goes here.")
                Button("Close") {
                    showInspector = false
                }
            }
            .padding()
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This example demonstrates how you can include multiple views within the inspector, such as text, dividers, and buttons, to create a more detailed and interactive inspector.&lt;/p&gt;
&lt;h4&gt;Practical Applications&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;.inspector&lt;/code&gt; modifier is particularly useful in applications where you need to provide additional context or controls without navigating away from the current view.&lt;br&gt;
For example, in a photo editing app, you could use an inspector to show editing tools and options.&lt;br&gt;
In a note-taking app, it could display metadata or additional settings related to the selected note.&lt;/p&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;.inspector&lt;/code&gt; modifier in SwiftUI is a powerful addition that allows developers to create more interactive and context-aware applications.&lt;br&gt;
By understanding and utilizing this modifier, you can enhance the user experience and provide more detailed and accessible information within your apps.&lt;/p&gt;</description>
  </item>
  <item>
    <title>Understanding reducers in Swift</title>    <link>https://wesleydegroot.nl/blog/understanding-reducers-in-swift</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/understanding-reducers-in-swift</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:07 +0200</pubDate>
    <category>Swift</category>
    <category>reduce</category>
    <description>&lt;p&gt;Reducers are a powerful concept in Swift, allowing developers to transform sequences of values into a single result.&lt;br&gt;
This technique is particularly useful when you need to perform operations like summing numbers, concatenating strings, or combining complex data structures.&lt;br&gt;
In this blog post, we'll explore what reducers are, how they work, and some practical examples of their use in Swift.&lt;/p&gt;
&lt;h2&gt;What is a Reducer?&lt;/h2&gt;
&lt;p&gt;A reducer is a function that takes a sequence of values and combines them into a single value.&lt;br&gt;
This is done by iteratively applying a closure that specifies how to combine each element of the sequence with an accumulating value.&lt;br&gt;
The most common use of reducers in Swift is through the &lt;code&gt;reduce&lt;/code&gt; function, which is available on all types that conform to the &lt;code&gt;Sequence&lt;/code&gt; protocol.&lt;/p&gt;
&lt;h2&gt;The &lt;code&gt;reduce&lt;/code&gt; Function&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://developer.apple.com/documentation/swift/array/reduce(_:_:)"&gt;&lt;code&gt;reduce&lt;/code&gt;&lt;/a&gt; function in Swift is defined as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;func reduce&amp;lt;Result&amp;gt;(
    _ initialResult: Result,
    _ nextPartialResult: (Result, Self.Element) throws -&amp;gt; Result
) rethrows -&amp;gt; Result&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;initialResult&lt;/code&gt;: The initial value to start the accumulation.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;nextPartialResult&lt;/code&gt;: A closure that combines the accumulating value with each element of the sequence.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here's a simple example of using &lt;code&gt;reduce&lt;/code&gt; to sum an array of numbers:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;let numbers = [1, 2, 3, 4, 5]
let sum = numbers.reduce(0) { $0 + $1 }
print(sum) // Outputs: 15&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, &lt;code&gt;0&lt;/code&gt; is the initial value, and the closure &lt;code&gt;{ $0 + $1 }&lt;/code&gt; specifies that each element should be added to the accumulating value².&lt;/p&gt;
&lt;h2&gt;Practical Examples&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Summing Nested Values&lt;/strong&gt;:&lt;br&gt;
Suppose you have an array of mailboxes, each containing a number of unread messages. You can use &lt;code&gt;reduce&lt;/code&gt; to calculate the total number of unread messages:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct Mailbox {
    let unreadMessages: Int
}

let mailboxes = [Mailbox(unreadMessages: 5), Mailbox(unreadMessages: 10), Mailbox(unreadMessages: 15)]
let totalUnreadCount = mailboxes.reduce(0) { $0 + $1.unreadMessages }
print(totalUnreadCount) // Outputs: 30&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Concatenating Strings&lt;/strong&gt;:&lt;br&gt;
You can also use &lt;code&gt;reduce&lt;/code&gt; to concatenate an array of strings into a single string:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;let words = ["Swift", "is", "awesome"]
let sentence = words.reduce("") { $0 + " " + $1 }
print(sentence) // Outputs: " Swift is awesome"&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Combining Complex Data Structures&lt;/strong&gt;:&lt;br&gt;
For more complex data structures, &lt;code&gt;reduce&lt;/code&gt; can be used to merge dictionaries:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;let dicts = [
    ["key1": "value1"],
    ["key2": "value2"],
    ["key3": "value3"]
]
let combinedDict = dicts.reduce([:]) { $0.merging($1) { (current, _) in current } }
print(combinedDict) // Outputs: ["key1": "value1", "key2": "value2", "key3": "value3"]&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Key Differencesbetween &lt;code&gt;reduce&lt;/code&gt; and &lt;code&gt;map&lt;/code&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Purpose:&lt;br&gt;
&lt;code&gt;map&lt;/code&gt;: Transforms each element of a sequence into a new element.&lt;br&gt;
&lt;code&gt;reduce&lt;/code&gt;: Combines all elements of a sequence into a single value.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Return Type:&lt;br&gt;
&lt;code&gt;map&lt;/code&gt;: Returns a new array with the transformed elements.&lt;br&gt;
&lt;code&gt;reduce&lt;/code&gt;: Returns a single value.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Usage:&lt;br&gt;
&lt;code&gt;map&lt;/code&gt;: Useful when you need to apply the same operation to each element and get a new array.&lt;br&gt;
&lt;code&gt;reduce&lt;/code&gt;: Useful when you need to summarize the elements into a single result.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Reducers are a versatile and powerful tool in Swift, enabling you to transform sequences into single values efficiently. Whether you're summing numbers, concatenating strings, or merging dictionaries, the &lt;code&gt;reduce&lt;/code&gt; function provides a clean and expressive way to achieve your goals. By mastering reducers, you can write more concise and readable code, making your Swift programming even more effective³.&lt;/p&gt;</description>
  </item>
  <item>
    <title>Creating a dynamic user interface for extensions in Aurora Editor</title>    <link>https://wesleydegroot.nl/blog/creating-a-dynamic-ui-for-extensions-in-aurora-editor</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/creating-a-dynamic-ui-for-extensions-in-aurora-editor</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:07 +0200</pubDate>
    <category>Aurora Editor</category>
    <category>In-depth</category>
    <description>&lt;p&gt;As some of you may know, I have been working on a IDE called Aurora Editor.&lt;br&gt;
It is a editor that is built using Swift and SwiftUI and is designed to be extensible and native.&lt;br&gt;
In this post, I will go trough the process I went to create a dynamic user interface for extensions in Aurora Editor.&lt;/p&gt;
&lt;p&gt;This is an (my first) in-depth post, let's get started!&lt;br&gt;
I hope it will be helpful for you, and i'd love to get feedback on this post.&lt;/p&gt;
&lt;h2&gt;The problem&lt;/h2&gt;
&lt;p&gt;It all started with a problem, how can a extension developer create a user interface for their extensions in Aurora Editor?&lt;br&gt;
I first started with a simple solution, using a SwiftUI view, from a Swift extension.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ExtensionView: View {
    var body: some View {
        Text("Hello, World!")
    }
}

// This is the view that will be displayed trough a extension window
ExtensionsManager.shared.sendEvent(
    event: "openWindow",
    parameters: [
        "view": ExtensionView()
    ]
)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This works great, but it does not allow views from a JavaScript extension to be displayed.&lt;br&gt;
I needed a way to create a dynamic user interface that could be used by both native (Swift) and JavaScript extensions.&lt;/p&gt;
&lt;p&gt;I first started with a WebView that would display the user interface, from a HTML string in a JavaScript extension, this is a working example from Aurora Editor.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-javascript"&gt;// This is the view that will be displayed trough a extension window
AuroraEditor.respond("openWindow", {
  view: "&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;&amp;lt;h1&amp;gt;Hello, World!&amp;lt;/h1&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;",
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This works fine, but it is not really native and it might not be the best solution because we want a (near) native experience.&lt;/p&gt;
&lt;h2&gt;What if I can use ... what can be used to create a dynamic user interface?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;SwiftUI: Unfortunately, SwiftUI cannot be shared between JavaScript and Swift&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;JavaScript: No this would mean we need to create a custom and difficult to maintain bridge between JavaScript and SwiftUI&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;JSON: This is a good idea, it is easy to parse and can? be used to create a dynamic user interface, let's try this!&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The birth of &lt;code&gt;DynamicUI&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;So JSON it is!   I created a new struct called &lt;code&gt;DynamicUI&lt;/code&gt; that can be used to create a dynamic user interface.&lt;/p&gt;
&lt;h4&gt;We need to create a way to convert JSON to a SwiftUI view.&lt;/h4&gt;
&lt;p&gt;We can probably achive this by following these steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Read the JSON (Data or String).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Parse the JSON.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a SwiftUI view from the parsed JSON.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Return the SwiftUI view.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Done!&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Sounds easy right? Let's try it!&lt;/h3&gt;
&lt;p&gt;We can't start with the list of steps above, we need to do them in a slightly different order.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a struct for the Dynamic UI Builder (Element definition in the JSON).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a view for the Dynamic UI Element (SwiftUI view for the element).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a DynamicUI? (Display the UI).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a DynamicUI. (Convert JSON to SwiftUI view).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use the DynamicUI.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use the DynamicUI in Aurora Editor.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Step 1: Creating a struct for the Dynamic UI Element&lt;/h2&gt;
&lt;p&gt;We need to create a struct that can be used to create a dynamic user interface element.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

public struct DynamicUIComponent: Codable, Hashable {
    public let type: String
    public let title: String?
    public let text: String?
    public let defaultValue: AnyCodable?
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Hey, what is &lt;a href="https://github.com/0xWDG/DynamicUI/blob/main/Sources/DynamicUI/Helpers/AnyCodable.swift"&gt;&lt;code&gt;AnyCodable&lt;/code&gt;&lt;/a&gt;?&lt;br&gt;
AnyCodeable is a type-erased codable value, it can be used to store any codable value (e.g. &lt;code&gt;String&lt;/code&gt;, &lt;code&gt;Int&lt;/code&gt;, &lt;code&gt;Bool&lt;/code&gt;, ...).&lt;/p&gt;
&lt;p&gt;We have a struct that can be used to create a dynamic user interface element. &lt;a href="https://github.com/0xWDG/DynamicUI/blob/main/Sources/DynamicUI/DynamicUIComponent.swift"&gt;Full source code&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Step 2: Create a Dynamic UI Element&lt;/h2&gt;
&lt;p&gt;We now need to create a view that can be used to create a dynamic user interface element.&lt;br&gt;
We create a new struct so we can pass all parameters required, and can append modifiers to the view.&lt;/p&gt;
&lt;p&gt;In this example, we will create a &lt;a href="https://github.com/0xWDG/DynamicUI/blob/main/Sources/DynamicUI/Views/DynamicText.swift#L28"&gt;&lt;code&gt;DynamicText&lt;/code&gt;&lt;/a&gt; element (to display text).&lt;br&gt;
I left out the &lt;a href="https://github.com/0xWDG/DynamicUI/blob/main/Sources/DynamicUI/Extensions/View.modifiers.swift#L21"&gt;&lt;code&gt;.dynamicUIModifiers()&lt;/code&gt;&lt;/a&gt; (modifier parser) code, since this will make the sample code to complex.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

public struct DynamicText: View {
    /// The component to display
    private let component: DynamicUIComponent

    /// Initialize the DynamicText
    init(_ component: DynamicUIComponent) {
        self.component = component
    }

    /// Generated body for SwiftUI
    public var body: some View {
        Text(.init(component.title ?? ""))
            .dynamicUIModifiers(component.modifiers)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 3: Create a DynamicUI?&lt;/h2&gt;
&lt;p&gt;Can we already create a dynamic user interface element using the &lt;code&gt;DynamicUIComponent&lt;/code&gt; and &lt;code&gt;DynamicText&lt;/code&gt; or do we need to create more? Unfortunately, we need to create more.&lt;br&gt;
We need to create a way to convert to parse the JSON and type-erease it.&lt;br&gt;
We create a new struct called &lt;code&gt;InternalDynamicUI&lt;/code&gt; that can be used to create a dynamic user interface.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct InternalDynamicUI: View {
    /// JSON Data
    public var json: Data?

    @State
    /// This state is used to store the layout
    private var layout: [DynamicUIComponent]?

    @State
    /// This state is used to store the error message
    private var error: String?

    /// Initialize the InternalDynamicUI
    var body: some View {
        VStack {
            if let layout = layout {
                buildView(for: layout)
            } else if let error = error {
                Text(error)
            } else {
                ProgressView()
                    .frame(width: 150, height: 150)
                    .padding()

                Text("Generating interface...")
            }
        }
        .onAppear {
            decodeJSON()
        }
    }

    /// Decode the JSON data
    private func decodeJSON() {
        do {
            if let json = json {
                self.layout = try JSONDecoder().decode(
                    [DynamicUIComponent].self,
                    from: json
                )
            }
        } catch {
            self.error = "Error decoding JSON: \(error)"
        }
    }

    /// Build a SwiftUI View based on the components
    /// - Parameter components: [UIComponent]
    /// - Returns: A SwiftUI View
    func buildView(for components: [DynamicUIComponent]) -&amp;gt; some View {
        return ForEach(components, id: \.self) { component in
            switch component.type {
            case "Text":
                DynamicText(component)
                    .environment(\.internalDynamicUIEnvironment, self)

            default:
                EmptyView()
            }
        }
    }
}

private struct InternalDynamicUIKey: EnvironmentKey {
    static let defaultValue: InternalDynamicUI = defaultValue
}

extension EnvironmentValues {
    var internalDynamicUIEnvironment: InternalDynamicUI {
        get { self[InternalDynamicUIKey.self] }
        set { self[InternalDynamicUIKey.self] = newValue }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 4: Create a DynamicUI&lt;/h2&gt;
&lt;p&gt;We have now created all components needed to create our dynamic user interface.&lt;/p&gt;
&lt;p&gt;We can now create a new struct called &lt;code&gt;DynamicUI&lt;/code&gt; that can be used to create a dynamic user interface.&lt;/p&gt;
&lt;p&gt;We create 2 initializers for the &lt;code&gt;DynamicUI&lt;/code&gt; struct, one for JSON Data and one for JSON String.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;public struct DynamicUI: View {
    /// DynamicUIComponent state change handler
    public typealias Handler = (DynamicUIComponent) -&amp;gt; Void

    /// JSON data
    public var json: Data?

    /// Initialize DynamicUI
    ///
    /// - Parameter json: JSON Data
    public init(json: Data? = nil) {
        self.json = json
    }

    /// Initialize DynamicUI
    /// 
    /// - Parameter json: JSON String
    public init(json: String? = nil) {
        // Convert the JSON string to data
        self.json = json?.data(using: .utf8)
    }

    /// Generated body for SwiftUI
    public var body: some View {
        AnyView(InternalDynamicUI(json: json))
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 5: Use the DynamicUI&lt;/h2&gt;
&lt;p&gt;We can now use the &lt;code&gt;DynamicUI&lt;/code&gt; to create a dynamic user interface.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ContentView: View {
    var body: some View {
        DynamicUI(
            json: """
            [
                {
                    "type": "Text",
                    "title": "Hello, World!"
                }
            ]
            """
        )
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 6: Use the DynamicUI in Aurora Editor&lt;/h2&gt;
&lt;p&gt;We can now use the &lt;code&gt;DynamicUI&lt;/code&gt; in Aurora Editor to create a dynamic user interface for extensions.&lt;/p&gt;
&lt;p&gt;We can pass the JSON string to the &lt;code&gt;DynamicUI&lt;/code&gt; and display the user interface.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-javascript"&gt;// This is the view that will be displayed trough a extension window
AuroraEditor.respond("openWindow", {
  view: "[{\"type\":\"Text\",\"title\":\"Hello, World!\"}]"
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can also pass the JSON as a object to the &lt;code&gt;DynamicUI&lt;/code&gt; and display the user interface.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-javascript"&gt;// This is the view that will be displayed trough a extension window
AuroraEditor.respond(
    "openWindow", 
    {
      view: [{
        "type": "Text",
        "title": "Hello, World!"
      }]
    }
);&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Playground&lt;/h2&gt;
&lt;p&gt;I have created a &lt;a href="https://github.com/0xWDG/DynamicUI/?tab=readme-ov-file#Playground"&gt;playground app&lt;/a&gt; that can be used to test the &lt;code&gt;DynamicUI&lt;/code&gt; framework.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Creating a dynamic user interface for extensions in Aurora Editor was a challenge, but it is really fun, it need some more improvements but the basics are there.&lt;br&gt;
I think i can improve some parts to make more use of @ViewBuilder.&lt;/p&gt;
&lt;p&gt;Resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/0xWDG/DynamicUI"&gt;DynamicUI&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/AuroraEditor/AuroraEditor"&gt;Aurora Editor&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Understanding Sendable in Swift</title>    <link>https://wesleydegroot.nl/blog/sendable-in-swift</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/sendable-in-swift</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:08 +0200</pubDate>
    <category>Swift</category>
    <category>Sendable</category>
    <description>&lt;p&gt;With the introduction of Swift 5.5, Apple brought significant enhancements to concurrency, including the &lt;strong&gt;Sendable protocol&lt;/strong&gt; and the &lt;strong&gt;@Sendable attribute&lt;/strong&gt;. These additions are crucial for ensuring thread safety in concurrent programming.&lt;br&gt;
Let's dive into what Sendable is, why it's important, and how you can use it in your Swift projects.&lt;/p&gt;
&lt;h2&gt;What is Sendable?&lt;/h2&gt;
&lt;p&gt;The &lt;strong&gt;Sendable protocol&lt;/strong&gt; in Swift indicates that a type is safe to be used concurrently.&lt;br&gt;
This means that instances of a Sendable type can be safely passed between different threads without causing data races or other concurrency issues. &lt;/p&gt;
&lt;h2&gt;Why is Sendable Important?&lt;/h2&gt;
&lt;p&gt;Concurrency introduces the challenge of ensuring that data accessed by multiple threads remains consistent and free from race conditions.&lt;br&gt;
The Sendable protocol helps the Swift compiler enforce these safety guarantees.&lt;br&gt;
By marking types as Sendable, you tell the compiler that these types can be safely shared across different threads.&lt;/p&gt;
&lt;h2&gt;Using @Sendable&lt;/h2&gt;
&lt;p&gt;The &lt;strong&gt;@Sendable attribute&lt;/strong&gt; can be applied to closures and functions to indicate that they are safe to be executed concurrently.&lt;br&gt;
When you mark a closure or function with @Sendable, the compiler enforces certain rules to ensure thread safety.&lt;br&gt;
For example, any values captured by a @Sendable closure must themselves be Sendable.&lt;/p&gt;
&lt;p&gt;Here's a simple example:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import Foundation

let task = Task { @Sendable in
    // This closure is marked as @Sendable
    print("Running a sendable task")
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, the closure passed to &lt;code&gt;Task&lt;/code&gt; is marked with @Sendable, ensuring that any captured values are safe for concurrent use.&lt;/p&gt;
&lt;h2&gt;Conforming to Sendable&lt;/h2&gt;
&lt;p&gt;To make a custom type conform to Sendable, you need to ensure that all its stored properties are also Sendable.&lt;br&gt;
Here's an example of a struct conforming to Sendable:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct MyData: Sendable {
    let value: Int
}

let data = MyData(value: 42)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this case, &lt;code&gt;MyData&lt;/code&gt; is a simple struct with a single &lt;code&gt;Int&lt;/code&gt; property, which is inherently Sendable.&lt;/p&gt;
&lt;h2&gt;Sendable in closures&lt;/h2&gt;
&lt;p&gt;When you use a closure that captures values, you need to ensure that those values are Sendable.&lt;/p&gt;
&lt;p&gt;Here's an example:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import Foundation

let value = 42

let closure: @Sendable () -&amp;gt; Int = {
    return value
}

let result = closure()

print(result) // Output: 42&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Practical Applications&lt;/h2&gt;
&lt;p&gt;Using Sendable and @Sendable is particularly useful in scenarios involving actors and tasks.&lt;br&gt;
For instance, when you pass data between different actors or tasks, ensuring that the data is Sendable helps maintain thread safety and prevents potential concurrency issues.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The introduction of the Sendable protocol and the @Sendable attribute in Swift marks a significant step forward in making concurrent programming safer and more reliable. By understanding and utilizing these features, you can write more robust and thread-safe Swift code.&lt;/p&gt;
&lt;p&gt;References:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swift/sendable"&gt;https://developer.apple.com/documentation/swift/sendable&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://www.massicotte.org/mistakes-with-concurrency"&gt;https://www.massicotte.org/mistakes-with-concurrency&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Mastering animations in SwiftUI</title>    <link>https://wesleydegroot.nl/blog/animations-in-swiftui</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/animations-in-swiftui</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:08 +0200</pubDate>
    <category>SwiftUI</category>
    <category>Animation</category>
    <description>&lt;p&gt;SwiftUI, Apple's declarative framework for building user interfaces, offers powerful and flexible tools for creating animations. Whether you're looking to add subtle transitions or complex, interactive animations, SwiftUI has you covered. In this blog post, we'll explore the basics of animations in SwiftUI, delve into more advanced techniques, and provide practical examples to help you get started.&lt;/p&gt;
&lt;h2&gt;Understanding Animations in SwiftUI&lt;/h2&gt;
&lt;p&gt;Animations in SwiftUI can be broadly categorized into two types: &lt;strong&gt;implicit&lt;/strong&gt; and &lt;strong&gt;explicit&lt;/strong&gt; animations.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Implicit Animations&lt;/strong&gt;: These are the simplest form of animations in SwiftUI. You define them using the &lt;code&gt;.animation()&lt;/code&gt; modifier. When you apply this modifier to a view, any changes to animatable properties (like position, size, or color) will automatically animate from their old values to the new ones.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt; struct ImplicitAnimationView: View {
     @State
     private var scale: CGFloat = 1.0

     var body: some View {
         VStack {
             Spacer()
             Circle()
                 .fill(Color.blue)
                 .frame(width: 100 * scale, height: 100 * scale)
                 .animation(.easeInOut(duration: 1.0), value: scale)
             Spacer()
             Button("Animate") {
                 scale = scale == 1.0 ? 2.0 : 1.0
             }
             .padding()
         }
     }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Explicit Animations&lt;/strong&gt;: These give you more control over the animation process. You use the &lt;code&gt;withAnimation&lt;/code&gt; function to explicitly define when and how the animation should occur.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt; struct ExplicitAnimationView: View {
     @State
     private var rotation: Double = 0

     var body: some View {
         VStack {
             Spacer()
             Rectangle()
                 .fill(Color.green)
                 .frame(width: 100, height: 100)
                 .rotationEffect(.degrees(rotation))
             Spacer()
             Button("Rotate") {
                 withAnimation(.easeInOut(duration: 1.0)) {
                     rotation += 45
                 }
             }
             .padding()
         }
     }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Advanced Animation Techniques&lt;/h2&gt;
&lt;p&gt;SwiftUI also supports more advanced animations, such as spring animations and custom transitions.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Spring Animations&lt;/strong&gt;: These animations simulate the effect of a spring, providing a more natural and dynamic feel. You can customize the stiffness and damping of the spring to achieve the desired effect.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt; struct SpringAnimationView: View {
     @State
     private var offset: CGFloat = 0

     var body: some View {
         VStack {
             Spacer()
             Circle()
                 .fill(Color.red)
                 .frame(width: 100, height: 100)
                 .offset(y: offset)
                 .animation(.spring(response: 0.5, dampingFraction: 0.6, blendDuration: 0), value: offset)
             Spacer()
             Button("Bounce") {
                 offset = offset == 0 ? 300 : 0
             }
             .padding()
         }
     }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Custom Transitions&lt;/strong&gt;: Transitions define how a view appears and disappears. SwiftUI provides several built-in transitions, but you can also create custom transitions by combining existing ones.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt; struct CustomTransitionView: View {
     @State
     private var showDetail = false

     var body: some View {
         VStack {
             Spacer()
             if showDetail {
                 Rectangle()
                     .fill(Color.purple)
                     .frame(width: 200, height: 200)
                     .transition(.asymmetric(insertion: .scale, removal: .opacity))
             }
             Spacer()
             Button("Toggle Detail") {
                 withAnimation {
                     showDetail.toggle()
                 }
             }
             .padding()
         }
     }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Practical Examples&lt;/h2&gt;
&lt;p&gt;Let's look at a practical example that combines several animation techniques to create a more complex and interactive UI.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct ComplexAnimationView: View {
    @State
    private var isAnimating = false

    var body: some View {
        VStack {
            Spacer()
            HStack {
                ForEach(0..&amp;lt;5) { index in
                    Circle()
                        .fill(isAnimating ? Color.red : Color.blue)
                        .frame(width: 50, height: 50)
                        .offset(y: isAnimating ? -100 : 0)
                        .animation(
                            .easeInOut(duration: 1.0)
                                .repeatForever(autoreverses: true)
                                .delay(Double(index) * 0.2),
                            value: isAnimating
                        )
                }
            }
            Spacer()
            Button("Start Animation") {
                isAnimating.toggle()
            }
            .padding()
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, we create a row of circles that bounce up and down with a delay between each circle, creating a wave-like effect. The animation repeats indefinitely, reversing direction each time.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;SwiftUI's animation capabilities are both powerful and easy to use, allowing you to create engaging and dynamic user interfaces with minimal code. By understanding the basics of implicit and explicit animations, and exploring more advanced techniques like spring animations and custom transitions, you can bring your SwiftUI apps to life.&lt;/p&gt;
&lt;p&gt;For more detailed information and examples, you can refer to the &lt;a href="https://developer.apple.com/documentation/swiftui/animations"&gt;Apple Developer Documentation&lt;/a&gt; and other resources like &lt;a href="https://www.hackingwithswift.com/quick-start/swiftui/how-to-create-basic-animations"&gt;Hacking with Swift&lt;/a&gt;.&lt;/p&gt;</description>
  </item>
  <item>
    <title>Building Calendo</title>    <link>https://wesleydegroot.nl/blog/building-calendo</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/building-calendo</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:09 +0200</pubDate>
    <category>Calendo</category>
    <description>&lt;p&gt;In this blog post, we'll explore the process of building Calendo, a calendar app that helps users view their schedules quickly and efficiently. We'll cover the key features of Calendo, the technologies used to develop it, and how you can get started with building your own calendar app.&lt;/p&gt;
&lt;h2&gt;The app idea "Calendo"&lt;/h2&gt;
&lt;p&gt;I really like to schedule my day, and I wanted to create a calendar app that would help me view my schedule quickly and efficiently. I wanted to create an app that would be easy to use, visually appealing, and provide a seamless experience for managing appointments and events.&lt;/p&gt;
&lt;h2&gt;Building Calendo&lt;/h2&gt;
&lt;h3&gt;Creating the User Interface&lt;/h3&gt;
&lt;p&gt;I wanted to create a clean and intuitive user interface for Calendo, I wanted to prototype the app quickly and therefore I decided to use SwiftUI.&lt;/p&gt;
&lt;p&gt;The main screen of Calendo displays a monthly calendar view with the current month and year at the top. Users can scroll through the calendar to view different months and tap on specific dates to view their appointments.&lt;/p&gt;
&lt;p&gt;Textual representation of the view i wanted.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-plaintext"&gt;&amp;lt; August 2024 &amp;gt;              [action]

Wk  Mon  Tue  Wed  Thu  Fri  Sat  Sun 
31  29   30   31   1    2    3    4
32  5    6    7    8    9    10   11
33  12   13   14   15   16   17   18
34  19   20   21   22   23   24   25
35  26   27   28   29   30   31   1
36  2    3    4    5    6    7    8
=====================================
┌───────────────────────────────────┐
│ 03 August 2024 - All day          │
│ Wesley's 34th Birthday            │
└───────────────────────────────────┘
┌───────────────────────────────────┐
│ 07 August 2024 - 19:00 - 23:00    │
│ Weekly Meeten en Drinken          │
| Cafe BAX, Ten Katestraat 119.     |
└───────────────────────────────────┘
┌───────────────────────────────────┐
│ 14 August 2024 - 19:00 - 23:00    │
│ Weekly Meeten en Drinken          │
| Cafe BAX, Ten Katestraat 119.     |
└───────────────────────────────────┘

....&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I wanted the days of the past and future to be dimmed out, and the current day to be highlighted.&lt;br&gt;
I also wanted to display appointments for each day in a list below the calendar view.&lt;br&gt;
A textual representation of the view i wanted for birthdays.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-plaintext"&gt;&amp;lt; Back                   ✏️
Wesley's 34th Birthday

        PHOTO  🎈
      OF CONTACT

Saturday, 3 August 2024
All day
Recurring, yearly.

 ┌───────────────────────────┐
 | Calendar:       Birthdays |
 | Notifications:         No |
 | Recurring:         Yearly |
 | Show as:             Free |
 | Status:       Appointment |
 └───────────────────────────┘&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A textual representation of the view i wanted for appointments.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-plaintext"&gt;&amp;lt; Back                   ✏️
Weekly Meeten en Drinken

Wednesday, 7 August 2024
19:00 - 23:00
Recurring, weekly.

┌───────────────────────────┐
| MAP VIEW         MAP VIEW |
| MAP VIEW         MAP VIEW |
├─────────────┐    MAP VIEW |
| Look Around |    MAP VIEW |
| Look Around |    MAP VIEW |
├─────────────┴─────────────┤
| Addres....     [NAVIGATE] |
└───────────────────────────┘

┌───────────────────────────┐
| Calendar:        Calendar |
| Notifications:         No |
| Recurring:         Weekly |
| Show as:             Busy |
| Status:       Appointment |
└───────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Implementing Core Features&lt;/h3&gt;
&lt;p&gt;I wanted Calendo to have the following core features:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Viewing Appointments&lt;/strong&gt;: Users can view their appointments for a specific day by tapping on a date in the calendar.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Adding Appointments&lt;/strong&gt;: Users can add new appointments by tapping on a date in the calendar and entering the details.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Editing Appointments&lt;/strong&gt;: Users can edit existing appointments by tapping on an appointment in the list view.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I also wanted to include additional features like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Lookaround, a feature that provides you with a 360-degree view of the location of your appointment.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Scroll trough your appointments and update the higlighted day in the calendar.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Problems encountered&lt;/h3&gt;
&lt;p&gt;I encountered several challenges while building Calendo, including:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Slow performance when loading large datasets.&lt;/strong&gt;&lt;br&gt;
Optimized data loading by using pagination (minus and plus one month).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Crashes due to specific appointments.&lt;/strong&gt;&lt;br&gt;
Fixed crashes by handling edge cases in the appointment data.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Slow performance when scrolling through the calendar.&lt;/strong&gt;&lt;br&gt;
Improved snappyness by doing certain calculations on the background, and only push the update back to the &lt;code&gt;@Published&lt;/code&gt; variable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Incorrect display of calendar view&lt;/strong&gt;&lt;br&gt;
On smaller screens, the calendar view was not displayed correctly.&lt;br&gt;
I use a &lt;code&gt;GeometryReader&lt;/code&gt; to calculate the size of the calendar view and adjust the layout accordingly.&lt;br&gt;
But on smaller screens the text size was bigger than the available space, so i had to adjust the font size based on the available space.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Translations&lt;/h3&gt;
&lt;p&gt;Calendo is available in multiple languages.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;English with help from &lt;a href="https://jruston.com"&gt;Jake Ruston&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Dutch (Nederlands)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;German (Deutsch)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;French (Français)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Greek (Ελληνικά) with help from &lt;a href="https://theopalios.com"&gt;Theo Palios&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Italian (Italiano) with help from &lt;a href="https://github.com/AngelK90"&gt;AngelK90&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Portuguese (Português)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Spanish (Español)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Chinese (中文)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Arabic (العربية)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;German, French, Italian, Portugese, Spanish, Chinese, Arabic are translated using Apple's &lt;a href="https://developer.apple.com/documentation/foundation/localization"&gt;Localisation&lt;/a&gt; framework, read more about it &lt;a href="/blog/Translation-framework-in-Swift"&gt;here (Translation framework in Swift)&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Download Calendo&lt;/h3&gt;
&lt;p&gt;You can download Calendo from the &lt;a class='button' href='https://apps.apple.com/us/app/calendo/id6651862759' target='_blank' class='button'&gt;&lt;i class="fa-brands fa-app-store"&gt;&lt;/i&gt; AppStore&lt;/a&gt;!.&lt;/p&gt;
&lt;h3&gt;Updates:&lt;/h3&gt;
&lt;p&gt;Interested in the development of Calendo? Read my previous next about &lt;a href="/blog/Fixing-slow-scrolling-in-Calendo"&gt;Fixing slow scrolling in Calendo&lt;/a&gt; or search on the &lt;a href="/blog/tag/Calendo"&gt;Tag: Calendo&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Technologies/Frameworks Used&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/foundation"&gt;Foundation&lt;/a&gt;&lt;br&gt;
Foundation is the base layer of all Apple frameworks, providing basic functionality like data storage, networking, and more.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui"&gt;SwiftUI&lt;/a&gt;&lt;br&gt;
SwiftUI is Apple's declarative framework for building user interfaces across all Apple platforms.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swift"&gt;Swift&lt;/a&gt;&lt;br&gt;
Swift is a Apple's programming language.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/eventkit"&gt;EventKit&lt;/a&gt;&lt;br&gt;
EventKit is a framework that allows you to access and manipulate calendar events and reminders.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/eventkitui"&gt;EventKitUI&lt;/a&gt;&lt;br&gt;
EventKitUI is a framework that provides a user interface for viewing and editing calendar events and reminders.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/mapkit"&gt;MapKit&lt;/a&gt;&lt;br&gt;
MapKit is a framework that allows you to embed Apple Maps directly into your app.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/oslog"&gt;OSLog&lt;/a&gt;&lt;br&gt;
OSLog is a unified logging system that provides a way to log messages from your app.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/0xWDG/OSLogViewer"&gt;OSLogViewer&lt;/a&gt;&lt;br&gt;
OSLogViewer is a tool that allows you to view and filter OSLog messages on your device.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/0xWDG/OnboardingKit"&gt;OnboardingKit&lt;/a&gt;&lt;br&gt;
OnboardingKit is a library that provides a customizable onboarding experience for your app.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/0xWDG/Admob-SwiftUI"&gt;Admob_SwiftUI&lt;/a&gt;&lt;br&gt;
Admob_SwiftUI is a library that allows you to easily integrate Google AdMob ads into your SwiftUI app.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Deep linking in SwiftUI</title>    <link>https://wesleydegroot.nl/blog/deep-linking-in-swiftui</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/deep-linking-in-swiftui</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:10 +0200</pubDate>
    <category>SwiftUI</category>
    <category>Navigation</category>
    <description>&lt;p&gt;Deep linking is a powerful feature that allows users to navigate directly to specific content within your app using URLs. In SwiftUI, implementing deep linking can enhance user experience by providing seamless navigation and quick access to desired content. This blog post will guide you through the process of setting up deep linking in your SwiftUI application.&lt;/p&gt;
&lt;h2&gt;What is Deep Linking?&lt;/h2&gt;
&lt;p&gt;Deep linking involves using URLs to link directly to specific pages or content within a mobile application. This can be particularly useful for marketing campaigns, notifications, or any scenario where you want to direct users to a specific part of your app.&lt;/p&gt;
&lt;h2&gt;Setting Up Deep Linking in SwiftUI&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Define URL Schemes&lt;/strong&gt;:&lt;br&gt;
To start, you need to define URL schemes in your app's &lt;code&gt;Info.plist&lt;/code&gt; file. This allows your app to recognize and handle specific URL patterns.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-xml"&gt;&amp;lt;key&amp;gt;CFBundleURLTypes&amp;lt;/key&amp;gt;
&amp;lt;array&amp;gt;
    &amp;lt;dict&amp;gt;
        &amp;lt;key&amp;gt;CFBundleURLSchemes&amp;lt;/key&amp;gt;
        &amp;lt;array&amp;gt;
            &amp;lt;string&amp;gt;demoApp&amp;lt;/string&amp;gt;
        &amp;lt;/array&amp;gt;
        &amp;lt;key&amp;gt;CFBundleURLName&amp;lt;/key&amp;gt;
        &amp;lt;string&amp;gt;nl.wesleydegroot.example&amp;lt;/string&amp;gt;
    &amp;lt;/dict&amp;gt;
&amp;lt;/array&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Handle Incoming URLs&lt;/strong&gt;:&lt;br&gt;
Next, you need to handle incoming URLs in your SwiftUI app. This can be done by implementing the &lt;code&gt;onOpenURL&lt;/code&gt; modifier, which allows you to respond to URL events.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;@main
struct demoApp: App {
    @StateObject
    private var router = Router()

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(router)
                .onOpenURL { url in
                    router.handleDeepLink(url: url)
                }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Routing Logic&lt;/strong&gt;:&lt;br&gt;
Create a router class to manage navigation based on the incoming URL.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;class Router: ObservableObject {
    @Published
    var currentView: String?

    func handleDeepLink(url: URL) {
        guard let host = url.host else { return }
        switch host {
        case "home":
            currentView = "home"
        case "profile":
            currentView = "profile"
        default:
            break
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Navigating to Views&lt;/strong&gt;:&lt;br&gt;
Use the &lt;code&gt;currentView&lt;/code&gt; property to navigate to the appropriate view in your SwiftUI app.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct ContentView: View {
    @EnvironmentObject
    var router: Router

    var body: some View {
        VStack {
            if router.currentView == "home" {
                HomeView()
            } else if router.currentView == "profile" {
                ProfileView()
            } else {
                DefaultView()
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Benefits of Deep Linking&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Improved User Experience&lt;/strong&gt;: Users can access specific content directly, reducing the number of steps needed to find what they are looking for.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Enhanced Engagement&lt;/strong&gt;: Deep links can be used in marketing campaigns to drive users to specific promotions or content.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Seamless Navigation&lt;/strong&gt;: Users can resume their activity from where they left off, even if they are returning to the app after a long time.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Deep linking via a notification&lt;/h2&gt;
&lt;p&gt;Deep linking can also be used in combination with notifications to direct users to specific content within your app. When a user taps on a notification, they can be taken directly to the relevant page or screen.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;let content = UNMutableNotificationContent()
    content.title = String(localized: "Your profile is 50% complete")
    content.body = String(localized: "Click here to complete your profile")
    content.userInfo = ["url": "demoApp://profile"]

let request = UNNotificationRequest(
    identifier: "DemoApp-ProfileSetupNotification", 
    content: content,
    trigger: UNTimeIntervalNotificationTrigger(
        timeInterval: 3600,
        repeats: false
    )
)

UNUserNotificationCenter.current().add(request)&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Implementing deep linking in SwiftUI can significantly enhance the usability and engagement of your app. By following the steps outlined above, you can set up deep linking to provide a more intuitive and efficient user experience. Happy coding!&lt;/p&gt;</description>
  </item>
  <item>
    <title>Navigation in SwiftUI</title>    <link>https://wesleydegroot.nl/blog/navigation-in-swiftui</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/navigation-in-swiftui</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:10 +0200</pubDate>
    <category>SwiftUI</category>
    <category>Navigation</category>
    <description>&lt;h3&gt;Navigating the World of SwiftUI: A Comprehensive Guide&lt;/h3&gt;
&lt;p&gt;SwiftUI, Apple's declarative framework for building user interfaces, has revolutionized the way developers create apps for iOS, macOS, watchOS, and tvOS. One of the core components of SwiftUI is its navigation system, which allows users to move seamlessly between different views. In this blog post, we'll explore the various navigation tools available in SwiftUI, including &lt;code&gt;NavigationView&lt;/code&gt;, &lt;code&gt;NavigationLink&lt;/code&gt;, and the newer &lt;code&gt;NavigationStack&lt;/code&gt; introduced in iOS 16.&lt;/p&gt;
&lt;h4&gt;1. &lt;strong&gt;Understanding NavigationView&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;NavigationView&lt;/code&gt; is the foundational component for navigation in SwiftUI. It provides a container that manages a stack of views, allowing users to push and pop views as they navigate through the app.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ContentView: View {
    var body: some View {
        NavigationView {
            VStack {
                Text("Home View")
                NavigationLink(destination: DetailView()) {
                    Text("Go to Detail View")
                }
            }
            .navigationTitle("Home")
        }
    }
}

struct DetailView: View {
    var body: some View {
        Text("Detail View")
            .navigationTitle("Detail")
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, &lt;code&gt;NavigationView&lt;/code&gt; wraps the content, and &lt;code&gt;NavigationLink&lt;/code&gt; creates a link to the &lt;code&gt;DetailView&lt;/code&gt;. When the link is tapped, the &lt;code&gt;DetailView&lt;/code&gt; is pushed onto the navigation stack.&lt;/p&gt;
&lt;h4&gt;2. &lt;strong&gt;Using NavigationLink&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;NavigationLink&lt;/code&gt; is a powerful tool that allows you to create links between views. It can be used in various ways, including programmatically.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;NavigationLink(destination: Text("New View")) {
    Text("Tap to navigate")
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also use &lt;code&gt;NavigationLink&lt;/code&gt; with more complex views and data passing.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct Item: Identifiable {
    var id = UUID()
    var name: String
}

struct ContentView: View {
    let items = [Item(name: "Item 1"), Item(name: "Item 2")]

    var body: some View {
        NavigationView {
            List(items) { item in
                NavigationLink(destination: Text(item.name)) {
                    Text(item.name)
                }
            }
            .navigationTitle("Items")
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3. &lt;strong&gt;Introducing NavigationStack&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;With iOS 16, Apple introduced &lt;code&gt;NavigationStack&lt;/code&gt;, which provides more flexibility and control over navigation. It allows for programmatic navigation and supports complex navigation paths.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ContentView: View {
    @State private var path = NavigationPath()

    var body: some View {
        NavigationStack(path: $path) {
            VStack {
                Button("Go to Detail") {
                    path.append("Detail")
                }
            }
            .navigationDestination(for: String.self) { value in
                if value == "Detail" {
                    DetailView()
                }
            }
        }
    }
}

struct DetailView: View {
    var body: some View {
        Text("Detail View")
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, &lt;code&gt;NavigationStack&lt;/code&gt; is used with a &lt;code&gt;NavigationPath&lt;/code&gt; to manage the navigation state. The &lt;code&gt;navigationDestination&lt;/code&gt; modifier specifies the destination view for each type of data in the path.&lt;/p&gt;
&lt;p&gt;I go into more detail about &lt;code&gt;NavigationStack&lt;/code&gt; in my &lt;a href="https://wesleydegroot.nl/blog/NavigationStack"&gt;NavigationStack post&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;4. &lt;strong&gt;Best Practices for Navigation in SwiftUI&lt;/strong&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Avoid Nesting NavigationViews&lt;/strong&gt;: Nesting &lt;code&gt;NavigationView&lt;/code&gt; inside another &lt;code&gt;NavigationView&lt;/code&gt; can lead to unexpected behavior. Instead, use a single &lt;code&gt;NavigationView&lt;/code&gt; at the root of your view hierarchy.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Use Navigation Titles&lt;/strong&gt;: Always set a navigation title for your views to provide context to the user.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Handle Deep Links&lt;/strong&gt;: Ensure your app can handle deep links by properly managing the navigation state.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;SwiftUI's navigation system is both powerful and flexible, allowing developers to create intuitive and seamless user experiences. Whether you're using &lt;code&gt;NavigationView&lt;/code&gt; for simple navigation or &lt;code&gt;NavigationStack&lt;/code&gt; for more complex scenarios, understanding these tools is essential for building modern SwiftUI apps.&lt;/p&gt;</description>
  </item>
  <item>
    <title>It's a wrap (2024)</title>    <link>https://wesleydegroot.nl/blog/its-a-wrap-2024</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/its-a-wrap-2024</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:11 +0200</pubDate>
    <category>Wrapped</category>
    <category>2024</category>
    <description>&lt;p&gt;On &lt;strong&gt;03 February 2024&lt;/strong&gt; I launched this (new) website, i wanted to post some more then once in 2 years,&lt;br&gt;
in 2024 I've released 64 articles!&lt;/p&gt;
&lt;h3&gt;February&lt;/h3&gt;
&lt;p&gt;In February I've released 11 articles&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/new-website"&gt;New Website&lt;/a&gt; (1 min) [Tags: &lt;a href="/blog/tag/Website" style="color: #00cccc"&gt;Website&lt;/a&gt;]&lt;br&gt;
Welcome to the new website! We hope you like it.  We've been working hard to make it as easy to use as possible.  If you have any feedback, please let me know.     This website is built with PHP. T...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/@EnvironmentVariable"&gt;Snippet: @EnvironmentVariable&lt;/a&gt; (1 min) [Tags: &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;, &lt;a href="/blog/tag/Observable" style="color: #ff00cc"&gt;Observable&lt;/a&gt;, &lt;a href="/blog/tag/MacOS" style="color: #006666"&gt;MacOS&lt;/a&gt;, &lt;a href="/blog/tag/EnvironmentVariable" style="color: #ffcc99"&gt;EnvironmentVariable&lt;/a&gt;, &lt;a href="/blog/tag/propertyWrapper" style="color: #00ff33"&gt;propertyWrapper&lt;/a&gt;]&lt;br&gt;
If you are creating commandline apps you sometimes need to acces the operating system environment variables, while you can use getenv(name) it can come in handy to use a property wrapper if you need...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/CryptoKit-TOTP"&gt;OTP Code Generation with CryptoKit: A Swift Approach&lt;/a&gt; (2 min) [Tags: &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;, &lt;a href="/blog/tag/CryptoKit" style="color: #66ffcc"&gt;CryptoKit&lt;/a&gt;, &lt;a href="/blog/tag/TOTP" style="color: #ffcc66"&gt;TOTP&lt;/a&gt;]&lt;br&gt;
In the realm of secure authentication, One  An OTP is a temporary password that is valid for a single use or a short period. It ensures that even if an attacker intercepts the password, they won't ...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Admob-in-SwiftUI"&gt;Implementing Admob in SwiftUI&lt;/a&gt; (6 min) [Tags: &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;, &lt;a href="/blog/tag/Admob" style="color: #6666ff"&gt;Admob&lt;/a&gt;]&lt;br&gt;
Recently i was working on a project that required me to implement Admob in a SwiftUI app.  I encountered some issues while implementing Admob in SwiftUI, so i decided to make a library Admob  The Ad...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Xcode-shortcuts"&gt;Xcode shortcuts&lt;/a&gt; (3 min) [Tags: &lt;a href="/blog/tag/Xcode" style="color: #99ccff"&gt;Xcode&lt;/a&gt;, &lt;a href="/blog/tag/shortcuts" style="color: #ffff66"&gt;shortcuts&lt;/a&gt;]&lt;br&gt;
As developers, we're always on the quest for efficiency. Whether you're a seasoned Xcode user or just starting out, harnessing the power of keyboard shortcuts can significantly enhance your coding ...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/SwiftUI-property-wrappers"&gt;SwiftUI property wrappers&lt;/a&gt; (5 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;, &lt;a href="/blog/tag/propertyWrapper" style="color: #00ff33"&gt;propertyWrapper&lt;/a&gt;]&lt;br&gt;
Let's dive into the fascinating world of SwiftUI property wrappers.  These powerful constructs allow you to manage data, state, and environment information in your SwiftUI views. 🚀   State: Thing...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/iCloud-drive-tips-and-tricks"&gt;iCloud Drive Tips &amp;amp; Tricks&lt;/a&gt; (5 min) [Tags: &lt;a href="/blog/tag/iCloud" style="color: #cc6633"&gt;iCloud&lt;/a&gt;]&lt;br&gt;
iCloud Drive is Apple's cloudiCloud Drive simplifies file management, collaboration, and access across your Apple ecosystem. Whether you're a student, professional, or creative, harness the power o...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/async-await"&gt;async/await&lt;/a&gt; (4 min) [Tags: &lt;a href="/blog/tag/async" style="color: #00ff33"&gt;async&lt;/a&gt;, &lt;a href="/blog/tag/await" style="color: #330000"&gt;await&lt;/a&gt;, &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;]&lt;br&gt;
Let's delve into the fascinating world of Swift's async/await.   This powerful feature, introduced in Swift 5.5, revolutionizes asynchronous programming, making it more intuitive and readable. Buckl...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/@dynamicMemberLookup"&gt;@dynamicMemberLookup&lt;/a&gt; (2 min) [Tags: &lt;a href="/blog/tag/dynamicMemberLookup" style="color: #cc9966"&gt;dynamicMemberLookup&lt;/a&gt;, &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;]&lt;br&gt;
Let's delve into the fascinating world of Swift's @dynamicMemberLookup. This attribute, introduced in Swift 4.2, provides a powerful mechanism for accessing properties and methods dynamically even th...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/SimpleNetworking"&gt;SimpleNetworking&lt;/a&gt; (5 min) [Tags: &lt;a href="/blog/tag/SimpleNetworking" style="color: #ff9999"&gt;SimpleNetworking&lt;/a&gt;, &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;, &lt;a href="/blog/tag/SwiftPM" style="color: #cc6666"&gt;SwiftPM&lt;/a&gt;]&lt;br&gt;
Today i write about my Swift package SimpleNetworking.   It's a simple networking library for Swift it is a wrapper around URLSession and provides a variety of functions to make easy network request...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Aurora-Editor"&gt;Aurora Editor&lt;/a&gt; (3 min) [Tags: &lt;a href="/blog/tag/Aurora Editor" style="color: #666633"&gt;Aurora Editor&lt;/a&gt;, &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;]&lt;br&gt;
As developers, we're always on the lookout for tools that enhance our productivity, streamline our workflows, and make coding a delightful experience. Enter Aurora Editor, a lightweight and fast ...&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;March&lt;/h3&gt;
&lt;p&gt;In March I've released 13 articles&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Closures-to-Async"&gt;Translating closures to async&lt;/a&gt; (4 min) [Tags: &lt;a href="/blog/tag/Async" style="color: #339933"&gt;Async&lt;/a&gt;, &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;]&lt;br&gt;
In this post, we will discuss how to translate closures to async in Swift.   In Swift, a continuation is a construct that allows you to suspend and resume execution of a piece of code at a later time...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Localizing-in-Xcode"&gt;Localizing In Xcode&lt;/a&gt; (1 min) [Tags: &lt;a href="/blog/tag/Xcode" style="color: #99ccff"&gt;Xcode&lt;/a&gt;, &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;, &lt;a href="/blog/tag/Localization" style="color: #339999"&gt;Localization&lt;/a&gt;]&lt;br&gt;
Your app's success hinges on delivering an exceptional experience to users across different locales. Localization is more than just translating text; it involves handling plurals, adapting content ...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/GIT"&gt;Dive into GIT&lt;/a&gt; (6 min) [Tags: &lt;a href="/blog/tag/git" style="color: #cc9900"&gt;git&lt;/a&gt;]&lt;br&gt;
Let's dive into the world of GIT. Whether you're a seasoned developer or just starting out, understanding these commands is crucial for efficient version control and collaboration. Below, I'll intro...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/@MainActor"&gt;What is @MainActor&lt;/a&gt; (4 min) [Tags: &lt;a href="/blog/tag/MainActor" style="color: #9933cc"&gt;MainActor&lt;/a&gt;]&lt;br&gt;
In the ever  @MainActor is a global actor that utilizes the main queue for executing its work. In practical terms, this means that methods or types marked with @MainActor can safely modify the UI bec...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/ContentUnavailableView"&gt;ContentUnavailableView&lt;/a&gt; (4 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;, &lt;a href="/blog/tag/ContentUnavailableView" style="color: #3333cc"&gt;ContentUnavailableView&lt;/a&gt;]&lt;br&gt;
In the ever  ContentUnavailableView is a specialized SwiftUI view designed specifically for presenting empty states. It's your go  In this example, if there are no search results while the query is n...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Pull-to-Refresh-in-SwiftUI"&gt;Pull-to-Refresh in SwiftUI&lt;/a&gt; (1 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;]&lt;br&gt;
PullIt allows users to refresh the content of a view by pulling down on it.   In this post, we will learn how to implement pull  Pull Let’s start with a simple example where you have a List display...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/TipKit"&gt;TipKit&lt;/a&gt; (2 min) [Tags: &lt;a href="/blog/tag/TipKit" style="color: #66ffff"&gt;TipKit&lt;/a&gt;, &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;]&lt;br&gt;
Let's delve into the fascinating world of TipKit, a powerful framework that empowers developers to create and customize app tips.   Whether you're aiming to introduce new features, reveal hidden fun...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Implementing-Sign-in-with-Apple"&gt;Implementing Sign in with Apple&lt;/a&gt; (5 min) [Tags: &lt;a href="/blog/tag/Sign in with Apple" style="color: #99cc99"&gt;Sign in with Apple&lt;/a&gt;, &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;]&lt;br&gt;
Sign in with Apple is a userIn this blog post, we'll explore how to integrate Sign in with Apple into your Swift  Sign in with Apple is a powerful authentication solution that simplifies user access ...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Default-values-for-UserDefaults"&gt;Default values for UserDefaults&lt;/a&gt; (1 min) [Tags: &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;, &lt;a href="/blog/tag/UserDefaults" style="color: #3366ff"&gt;UserDefaults&lt;/a&gt;]&lt;br&gt;
UserDefaults (NSUserDefaults) is a go  UserDefaults is a simple key  `swift let userDefaults = UserDefaults.standard userDefaults.register(     defaults: [         "selectedPokemon": "Pikachu",     ...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/SwiftUI-ViewModifiers"&gt;SwiftUI ViewModifiers&lt;/a&gt; (5 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;, &lt;a href="/blog/tag/ViewModifiers" style="color: #999999"&gt;ViewModifiers&lt;/a&gt;]&lt;br&gt;
SwiftUI ViewModifiers are a powerful tool for customizing and enhancing views in your app. They allow you to encapsulate common view modifications into reusable, composable units, making it easy to ...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Subscript-in-Swift"&gt;Subscripts in Swift&lt;/a&gt; (1 min) [Tags: &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;]&lt;br&gt;
Subscripts provide a convenient way to access the elements of a collection, list, or sequence directly by index. Whether you’re dealing with arrays, dictionaries, or custom types, subscripts allow ...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Monitor-network-in-SwiftUI"&gt;How to monitor network in SwiftUI&lt;/a&gt; (3 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;, &lt;a href="/blog/tag/Network" style="color: #ffcc99"&gt;Network&lt;/a&gt;]&lt;br&gt;
Network monitoring is crucial for maintaining robust and reliable applications.   In this blog post, we’ll explore how to implement network monitoring in Swift using Apple’s native Network framew...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/LabeledContent-in-SwiftUI"&gt;LabeledContent in SwiftUI&lt;/a&gt; (2 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;, &lt;a href="/blog/tag/LabeledContent" style="color: #ccff00"&gt;LabeledContent&lt;/a&gt;]&lt;br&gt;
As developers, we often need to display a value alongside a label, and manually arranging this can be difficult.   But LabeledContent, a powerful and versatile view that simplifies this common UI pat...&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;April&lt;/h3&gt;
&lt;p&gt;In April I've released 6 articles&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/ExpressibleByStringLiteral-URL"&gt;ExpressibleByStringLiteral URL&lt;/a&gt; (1 min) [Tags: &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;, &lt;a href="/blog/tag/ExpressibleByStringLiteral" style="color: #ff0066"&gt;ExpressibleByStringLiteral&lt;/a&gt;]&lt;br&gt;
The ExpressibleByStringLiteral protocol is a powerful feature in Swift that allows us to create custom types directly from string literals. Essentially, it enables us to initialize instances of our ...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/self-Self-Self.self-in-Swift"&gt;self, Self, and Self.self in Swift&lt;/a&gt; (2 min) [Tags: &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;, &lt;a href="/blog/tag/self" style="color: #660066"&gt;self&lt;/a&gt;]&lt;br&gt;
Let's dive into the fascinating world of self, Self, and Self.self in Swift.   These seemingly similar constructs have distinct meanings and use cases.  When you're inside an instance method or prope...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Appril-festival-2024"&gt;Appril Festival 2024&lt;/a&gt; (5 min) [Tags: &lt;a href="/blog/tag/Conference" style="color: #9966cc"&gt;Conference&lt;/a&gt;, &lt;a href="/blog/tag/Appril Festival" style="color: #660066"&gt;Appril Festival&lt;/a&gt;, &lt;a href="/blog/tag/Appsterdam" style="color: #cc00ff"&gt;Appsterdam&lt;/a&gt;]&lt;br&gt;
As you may (or may not yet) know, I'd love to support and help the tech industry, today I'd like to highlight Appril Festival, i have volunteered a several times and it's a great event to attend, lea...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/if-case-let"&gt;If case let&lt;/a&gt; (3 min) [Tags: &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;]&lt;br&gt;
This concise syntax provides a convenient shortcut when you want to match a single case without writing a full  Imagine you're working with the Swift Result type, capturing the results of a throwing ...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Variadic-Parameters"&gt;Variadic Parameters&lt;/a&gt; (3 min) [Tags: &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;]&lt;br&gt;
These versatile constructs allow us to handle a variable number of arguments of the same type within a function.   In Swift, variadic parameters enable a function to accept zero or more values of a s...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Swift-Generics"&gt;Generics in Swift&lt;/a&gt; (3 min) [Tags: &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;, &lt;a href="/blog/tag/Generics" style="color: #0066cc"&gt;Generics&lt;/a&gt;]&lt;br&gt;
Swift, offers a powerful feature called generics that greatly enhances code reusability, efficiency, and safety. In this blog post, we will dive deep into generics and explore how they can be levera...&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;May&lt;/h3&gt;
&lt;p&gt;In May I've released 1 articles&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Handle-hyperlinks-in-SwiftUI"&gt;Handle hyperlinks in SwiftUI&lt;/a&gt; (2 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;, &lt;a href="/blog/tag/Hyperlink" style="color: #66cc33"&gt;Hyperlink&lt;/a&gt;]&lt;br&gt;
If you have a SwiftUI application and you present text with a URL, have you notices how the URL is being opened by Safari and not an in  An action that opens a URL. OpenURLAction can be used to creat...&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;July&lt;/h3&gt;
&lt;p&gt;In July I've released 3 articles&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/OSLog"&gt;Loging using OSLog&lt;/a&gt; (4 min) [Tags: &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;, &lt;a href="/blog/tag/OSLog" style="color: #ccffcc"&gt;OSLog&lt;/a&gt;]&lt;br&gt;
OSLog is a Swift API that provides a unified logging system for all Apple platforms. It is a replacement for the older print and NSLog functions. OSLog is a more efficient and secure logging system t...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/@Environment"&gt;@Environment variables&lt;/a&gt; (3 min) [Tags: &lt;a href="/blog/tag/Environment" style="color: #009999"&gt;Environment&lt;/a&gt;, &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;]&lt;br&gt;
SwiftUI provides a way to pass data down the view hierarchy using @Environment variables. These variables are environment When you create an @Environment variable, SwiftUI automatically manages its ...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/@ViewBuilder"&gt;@ViewBuilder in Swift&lt;/a&gt; (3 min) [Tags: &lt;a href="/blog/tag/ViewBuilder" style="color: #cc33cc"&gt;ViewBuilder&lt;/a&gt;, &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;]&lt;br&gt;
SwiftUI, Apple's declarative framework for building user interfaces, introduces several powerful tools to streamline UI development. One such tool is the @ViewBuilder attribute, which simplifies the ...&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;August&lt;/h3&gt;
&lt;p&gt;In August I've released 5 articles&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/CoreSpotlight"&gt;CoreSpotlight&lt;/a&gt; (6 min) [Tags: &lt;a href="/blog/tag/CoreSpotlight" style="color: #ff9966"&gt;CoreSpotlight&lt;/a&gt;, &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;]&lt;br&gt;
CoreSpotlight is a framework provided by Apple that allows you to index and search content within your app. It provides a way to make your app's content searchable and discoverable by users through t...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/map,flatMap,compactMap"&gt;Difference between map, flatMap, compactMap&lt;/a&gt; (2 min) [Tags: &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;, &lt;a href="/blog/tag/map" style="color: #3366cc"&gt;map&lt;/a&gt;, &lt;a href="/blog/tag/flatmap" style="color: #ff6633"&gt;flatmap&lt;/a&gt;, &lt;a href="/blog/tag/compactMap" style="color: #ffcc99"&gt;compactMap&lt;/a&gt;]&lt;br&gt;
In this post, we'll explore the differences between map, flatMap, and compactMap in Swift. These three methods are commonly used when working with collections, and understanding their distinctions is...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/enums"&gt;Enums&lt;/a&gt; (7 min) [Tags: &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;, &lt;a href="/blog/tag/Enums" style="color: #3333ff"&gt;Enums&lt;/a&gt;]&lt;br&gt;
In this post, we'll explore enums in Swift, a powerful feature that allows you to define a group of related values. Enums are commonly used to represent a fixed set of options or states in your code,...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/@frozen"&gt;What is @frozen&lt;/a&gt; (4 min) [Tags: &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;, &lt;a href="/blog/tag/Frozen" style="color: #6699cc"&gt;Frozen&lt;/a&gt;]&lt;br&gt;
In this post, we'll explore the @frozen attribute in Swift, a compiler directive that can be applied to enum declarations to optimize memory layout and improve performance. By marking an enum as @fro...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/iOS-settings-URLs"&gt;iOS Settings URL's&lt;/a&gt; (93 min) [Tags: &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;, &lt;a href="/blog/tag/iOS" style="color: #33cc66"&gt;iOS&lt;/a&gt;, &lt;a href="/blog/tag/Settings" style="color: #ffff00"&gt;Settings&lt;/a&gt;]&lt;br&gt;
This is a list of internal URLs for settings on your iPhone, iPad, ... Please note that this list will be updated if required, if you see something which is not right, please drop me a message and i...&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;September&lt;/h3&gt;
&lt;p&gt;In September I've released 4 articles&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Why-you-should-avoid-AnyView-in-SwiftUI"&gt;Why You Should Avoid Using AnyView in SwiftUI&lt;/a&gt; (3 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;, &lt;a href="/blog/tag/AnyView" style="color: #9900ff"&gt;AnyView&lt;/a&gt;]&lt;br&gt;
SwiftUI, Apple's declarative framework for building user interfaces, introduces a variety of new concepts and tools for developers.   One such tool is AnyView, a typeWhile AnyView can be useful in c...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/UI-Testing-in-Swift"&gt;A Guide to UI Testing in Swift&lt;/a&gt; (9 min) [Tags: &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;, &lt;a href="/blog/tag/UITest" style="color: #ff6633"&gt;UITest&lt;/a&gt;, &lt;a href="/blog/tag/XCUITest" style="color: #99ff00"&gt;XCUITest&lt;/a&gt;]&lt;br&gt;
User Interface (UI) testing is a crucial aspect of app development, ensuring that your app's interface behaves as expected under various conditions.   This guide will walk you through the basics of ...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Understanding-assertions-in-Swift"&gt;Understanding assertions in Swift&lt;/a&gt; (2 min) [Tags: &lt;a href="/blog/tag/Assert" style="color: #cc33ff"&gt;Assert&lt;/a&gt;, &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;]&lt;br&gt;
Assertions are a powerful tool in Swift that help developers ensure their code behaves as expected during runtime. They allow you to test assumptions and catch potential issues early in the developme...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Safely-unwrap-optional-values-in-SwiftUI-bindings"&gt;Safely unwrap optional values in SwiftUI bindings&lt;/a&gt; (3 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;, &lt;a href="/blog/tag/Binding" style="color: #99ffff"&gt;Binding&lt;/a&gt;]&lt;br&gt;
Bindings are a powerful feature in SwiftUI that allow you to connect your views to your data. However, when working with optional values, you need to be careful to avoid crashes due to force unwrappi...&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;October&lt;/h3&gt;
&lt;p&gt;In October I've released 12 articles&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Hacktoberfest-2024"&gt;Hacktoberfest 2024&lt;/a&gt; (2 min) [Tags: &lt;a href="/blog/tag/Hacktoberfest" style="color: #996633"&gt;Hacktoberfest&lt;/a&gt;]&lt;br&gt;
As the leaves change color and the air turns crisp, developers around the world gear up for one of the most exciting events in the openThis annual celebration, organized by DigitalOcean, GitHub, and...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Swift-Package-DynamicUI"&gt;SwiftUI Development with DynamicUI&lt;/a&gt; (2 min) [Tags: &lt;a href="/blog/tag/SwiftPM" style="color: #cc6666"&gt;SwiftPM&lt;/a&gt;, &lt;a href="/blog/tag/DynamicUI" style="color: #99cc33"&gt;DynamicUI&lt;/a&gt;]&lt;br&gt;
In the everOne tool that stands out in achieving these goals is DynamicUI, a Swift Package designed to create SwiftUI user interfaces dynamically from JSON files.   Let's explore how DynamicUI can t...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Swift-Package-Colors"&gt;Simplifying multiplatform Colors&lt;/a&gt; (1 min) [Tags: &lt;a href="/blog/tag/SwiftPM" style="color: #cc6666"&gt;SwiftPM&lt;/a&gt;, &lt;a href="/blog/tag/Colors" style="color: #666699"&gt;Colors&lt;/a&gt;]&lt;br&gt;
Sometimes you are building for multiple platforms and you want to use the same color across all of them.   This is where the Colors package comes in handy.   It provides a set of predefined system c...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Swift-Package-OnboardingKit"&gt;Simplifying App Onboarding with OnboardingKit&lt;/a&gt; (4 min) [Tags: &lt;a href="/blog/tag/SwiftPM" style="color: #cc6666"&gt;SwiftPM&lt;/a&gt;, &lt;a href="/blog/tag/OnboardingKit" style="color: #9966cc"&gt;OnboardingKit&lt;/a&gt;]&lt;br&gt;
Creating a smooth and engaging onboarding experience is crucial for any app.   OnboardingKit, a SwiftUI package available on GitHub, provides a comprehensive solution to help developers create these...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Swift-Package-GameControllerKit"&gt;Simplifying Game Controller Integration with GameControllerKit&lt;/a&gt; (3 min) [Tags: &lt;a href="/blog/tag/SwiftPM" style="color: #cc6666"&gt;SwiftPM&lt;/a&gt;, &lt;a href="/blog/tag/GameControllerKit" style="color: #9999cc"&gt;GameControllerKit&lt;/a&gt;]&lt;br&gt;
In the everEnter GameControllerKit, a Swift package designed to streamline this process for developers working on iOS, macOS, and tvOS.   Let's dive into what makes this toolkit a game  GameControll...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Swift-Package-CachedAsyncImage"&gt;Enhancing SwiftUI with CachedAsyncImage&lt;/a&gt; (2 min) [Tags: &lt;a href="/blog/tag/SwiftPM" style="color: #cc6666"&gt;SwiftPM&lt;/a&gt;, &lt;a href="/blog/tag/CachedAsyncImage" style="color: #cc9966"&gt;CachedAsyncImage&lt;/a&gt;]&lt;br&gt;
In the world of mobile app development, performance and user experience are paramount.   One common challenge developers face is efficiently loading and caching images.   Enter CachedAsyncImage, a S...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Swift-Package-Inspect"&gt;Inspect: A Powerful Tool for SwiftUI Developers&lt;/a&gt; (2 min) [Tags: &lt;a href="/blog/tag/SwiftPM" style="color: #cc6666"&gt;SwiftPM&lt;/a&gt;, &lt;a href="/blog/tag/Inspect" style="color: #3399cc"&gt;Inspect&lt;/a&gt;]&lt;br&gt;
As SwiftUI continues to evolve, developers are constantly seeking tools to streamline their workflow and enhance their debugging capabilities.   One such tool that stands out is Inspect, a Swift Pac...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/SwiftLeeds-2024-1"&gt;SwiftLeeds 2024, Day 1&lt;/a&gt; (2 min) [Tags: &lt;a href="/blog/tag/SwiftLeeds" style="color: #00ff66"&gt;SwiftLeeds&lt;/a&gt;]&lt;br&gt;
It's that time of year again!   SwiftLeeds 2024 is here, and in this post, i'll be sharing my thoughts on the first day of the conference.     Key Takeaways:                                         ...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/SwiftLeeds-2024-2"&gt;SwiftLeeds 2024, Day 2&lt;/a&gt; (3 min) [Tags: &lt;a href="/blog/tag/SwiftLeeds" style="color: #00ff66"&gt;SwiftLeeds&lt;/a&gt;]&lt;br&gt;
It's that time of year again!   SwiftLeeds 2024 is here, and in this post, i'll be sharing my thoughts on the second (and last) day of the conference.     Hidde shared his insights on how to improve...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Understanding-Swift-Package"&gt;Understanding Package.swift&lt;/a&gt; (9 min) [Tags: &lt;a href="/blog/tag/SwiftPM" style="color: #cc6666"&gt;SwiftPM&lt;/a&gt;, &lt;a href="/blog/tag/Package.swift" style="color: #33cc33"&gt;Package.swift&lt;/a&gt;]&lt;br&gt;
If you're diving into Swift development, you've likely encountered Package.swift.   This file is the cornerstone of Swift Package Manager (SPM), Apple's tool for managing Swift code dependencies.   ...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Translation-framework-in-Swift"&gt;Translation framework in Swift&lt;/a&gt; (4 min) [Tags: &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;, &lt;a href="/blog/tag/Translation" style="color: #66cc99"&gt;Translation&lt;/a&gt;, &lt;a href="/blog/tag/Framework" style="color: #006633"&gt;Framework&lt;/a&gt;]&lt;br&gt;
With the release of iOS 17.4, Apple introduced the Translation framework, a powerful tool that allows developers to integrate text translation capabilities directly into their Swift applications. T...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/async-buttons-in-SwiftUI"&gt;Building an Asynchronous Button in SwiftUI&lt;/a&gt; (5 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;, &lt;a href="/blog/tag/Asynchronus" style="color: #33cc33"&gt;Asynchronus&lt;/a&gt;]&lt;br&gt;
Sometimes, we need to perform asynchronous tasks when a button is tapped in a SwiftUI application. For example, fetching data from a network request, saving data to a database, or performing any lon...&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;November&lt;/h3&gt;
&lt;p&gt;In November I've released 4 articles&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/the-inspector-modifier-in-SwiftUI"&gt;Exploring the .inspector Modifier in SwiftUI&lt;/a&gt; (3 min) [Tags: &lt;a href="/blog/tag/Inspector" style="color: #99ff00"&gt;Inspector&lt;/a&gt;, &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;]&lt;br&gt;
SwiftUI continues to evolve, bringing new and powerful tools to developers.   One such tool is the .inspector modifier, introduced to enhance the user interface by providing contextThis article will...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Understanding-reducers-in-Swift"&gt;Understanding reducers in Swift&lt;/a&gt; (3 min) [Tags: &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;, &lt;a href="/blog/tag/reduce" style="color: #99cccc"&gt;reduce&lt;/a&gt;]&lt;br&gt;
Reducers are a powerful concept in Swift, allowing developers to transform sequences of values into a single result.   This technique is particularly useful when you need to perform operations like ...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Creating-a-dynamic-ui-for-extensions-in-Aurora-Editor"&gt;Creating a dynamic user interface for extensions in Aurora Editor&lt;/a&gt; (8 min) [Tags: &lt;a href="/blog/tag/Aurora Editor" style="color: #666633"&gt;Aurora Editor&lt;/a&gt;, &lt;a href="/blog/tag/In-depth" style="color: #666666"&gt;In-depth&lt;/a&gt;]&lt;br&gt;
As some of you may know, I have been working on a IDE called Aurora Editor.   It is a editor that is built using Swift and SwiftUI and is designed to be extensible and native.  In this post, I will ...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Sendable-in-Swift"&gt;Understanding Sendable in Swift&lt;/a&gt; (3 min) [Tags: &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;, &lt;a href="/blog/tag/Sendable" style="color: #cc6699"&gt;Sendable&lt;/a&gt;]&lt;br&gt;
With the introduction of Swift 5.5, Apple brought significant enhancements to concurrency, including the Sendable protocol and the @Sendable attribute. These additions are crucial for ensuring threa...&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;December&lt;/h3&gt;
&lt;p&gt;In December I've released 5 articles&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Animations-in-SwiftUI"&gt;Mastering animations in SwiftUI&lt;/a&gt; (4 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;, &lt;a href="/blog/tag/Animation" style="color: #cccccc"&gt;Animation&lt;/a&gt;]&lt;br&gt;
SwiftUI, Apple's declarative framework for building user interfaces, offers powerful and flexible tools for creating animations. Whether you're looking to add subtle transitions or complex, interact...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Building-Calendo"&gt;Building Calendo&lt;/a&gt; (5 min) [Tags: &lt;a href="/blog/tag/Calendo" style="color: #999966"&gt;Calendo&lt;/a&gt;]&lt;br&gt;
In this blog post, we'll explore the process of building Calendo, a calendar app that helps users view their schedules quickly and efficiently. We'll cover the key features of Calendo, the technolog...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Deep-linking-in-SwiftUI"&gt;Deep linking in SwiftUI&lt;/a&gt; (3 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;, &lt;a href="/blog/tag/Navigation" style="color: #996699"&gt;Navigation&lt;/a&gt;]&lt;br&gt;
Deep linking is a powerful feature that allows users to navigate directly to specific content within your app using URLs. In SwiftUI, implementing deep linking can enhance user experience by providi...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Navigation-in-SwiftUI"&gt;Navigation in SwiftUI&lt;/a&gt; (3 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;, &lt;a href="/blog/tag/Navigation" style="color: #996699"&gt;Navigation&lt;/a&gt;]&lt;br&gt;
SwiftUI, Apple's declarative framework for building user interfaces, has revolutionized the way developers create apps for iOS, macOS, watchOS, and tvOS. One of the core components of SwiftUI is it...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Its-a-wrap-2024"&gt;It's a wrap (2024)&lt;/a&gt; (1 min) [Tags: &lt;a href="/blog/tag/Wrapped" style="color: #9999ff"&gt;Wrapped&lt;/a&gt;, &lt;a href="/blog/tag/2024" style="color: #009933"&gt;2024&lt;/a&gt;]&lt;br&gt;
On 03 February 2024 I launched this (new) website, i wanted to post some more then once in 2 years,  in 2024 I've released 64 articles!  In February I've released 11 articles             In March I'v...&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Reading minutes per month&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;February: 37 minutes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;March: 39 minutes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;April: 17 minutes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;May: 2 minutes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;July: 10 minutes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;August: 112 minutes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;September: 17 minutes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;October: 39 minutes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;November: 17 minutes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;December: 16 minutes&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The total reading time of all articles is about 306 minutes (5.1 hours)!&lt;/p&gt;</description>
  </item>
  <item>
    <title>Apple version numbers</title>    <link>https://wesleydegroot.nl/blog/apple-version-numbers</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/apple-version-numbers</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:11 +0200</pubDate>
    <category>Apple</category>
    <category>version numbers</category>
    <description>&lt;p&gt;As you may know, Apple releases new versions of its operating systems every year. The version numbers are not always the same across all platforms, and they don't always follow the same pattern. Here is a list of the version numbers for Apple's operating systems, starting from 2007.&lt;br&gt;
&lt;br&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Year&lt;/th&gt;
&lt;th&gt;iOS&lt;/th&gt;
&lt;th&gt;iPadOS&lt;/th&gt;
&lt;th&gt;macOS&lt;/th&gt;
&lt;th&gt;tvOS&lt;/th&gt;
&lt;th&gt;visionOS&lt;/th&gt;
&lt;th&gt;watchOS&lt;/th&gt;
&lt;th&gt;Xcode&lt;/th&gt;
&lt;th&gt;iPhone&lt;/th&gt;
&lt;th&gt;Watch&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2025&lt;/td&gt;
&lt;td&gt;26&lt;/td&gt;
&lt;td&gt;26&lt;/td&gt;
&lt;td&gt;26: Sequoia&lt;/td&gt;
&lt;td&gt;26&lt;/td&gt;
&lt;td&gt;26&lt;/td&gt;
&lt;td&gt;26&lt;/td&gt;
&lt;td&gt;26&lt;/td&gt;
&lt;td&gt;??&lt;/td&gt;
&lt;td&gt;??&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2024&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;15: Sequoia&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2023&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;14: Sonoma&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2022&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;13: Ventura&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2021&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;12: Monterey&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2020&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;11: Big Sur&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2019&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;10.15: Catalina&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2018&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;10.14: Mojave&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;XR &amp;amp; XS&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2017&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;10.13: High Sierra&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;8 &amp;amp; X&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2016&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;10.12: Sierra&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2015&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;10.11: El Capitan&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;6s&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2014&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;10.10: Yosemite&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;1*&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2013&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;10.9: Mavericks&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;4.6 &amp;amp; 5.0&lt;/td&gt;
&lt;td&gt;5s &amp;amp; 5c&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2012&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;10.8: Mountain Lion&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;4.3, 4.4, 4.5&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2011&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;10.7: Lion&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;4 &amp;amp; 4.1&lt;/td&gt;
&lt;td&gt;4s&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2010&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;3.2&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2009&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;10.6: Snow Leopard&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;5?&lt;/td&gt;
&lt;td&gt;3GS&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2008&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;3.1&lt;/td&gt;
&lt;td&gt;3G&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2007&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;10.5: Leopard&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;2G&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</description>
  </item>
  <item>
    <title>Accessibility in SwiftUI</title>    <link>https://wesleydegroot.nl/blog/accessibility-in-swiftui</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/accessibility-in-swiftui</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:12 +0200</pubDate>
    <category>Accessibility</category>
    <category>SwiftUI</category>
    <description>&lt;p&gt;Accessibility in app development is all about making sure everyone, regardless of their abilities, can use your app. Apple’s SwiftUI framework makes integrating accessibility features more streamlined. Here, we'll explore key strategies to make your SwiftUI apps more inclusive.&lt;/p&gt;
&lt;h3&gt;Understanding Accessibility&lt;/h3&gt;
&lt;p&gt;Accessibility isn't just a checklist; it’s a mindset. Think of it as designing for the broadest range of human abilities and environments. It's about empathy, respecting your users, and enabling them to interact with your app seamlessly.&lt;/p&gt;
&lt;h3&gt;Best Practices for Accessibility&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Use Dynamic Type for adjustable text sizes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Ensure sufficient contrast between text and background colors.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Provide text labels for color-coded information.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Make interactive elements large enough for easy tapping.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Important modifiers&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/accessibilitylabel(_:)"&gt;&lt;code&gt;.accessibilityLabel(_:)&lt;/code&gt;&lt;/a&gt;: Provides a descriptive label for UI elements.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/accessibilityvalue(_:)"&gt;&lt;code&gt;.accessibilityValue(_:)&lt;/code&gt;&lt;/a&gt;: Represents the current value of an interactive element.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/accessibilityhint(_:)"&gt;&lt;code&gt;.accessibilityHint(_:)&lt;/code&gt;&lt;/a&gt;: Offers hints about how to use a UI element.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/accessibilityaction(_:_:)"&gt;&lt;code&gt;.accessibilityAction(_:_:)&lt;/code&gt;&lt;/a&gt;: Allows you to define custom actions for accessibility.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/accessibilitysortpriority(_:)"&gt;&lt;code&gt;.accessibilitySortPriority(_:)&lt;/code&gt;&lt;/a&gt;: Sets the sort priority of an accessibility element.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Accessibility in SwiftUI: Getting Started&lt;/h3&gt;
&lt;p&gt;SwiftUI simplifies the process with built-in accessibility modifiers.&lt;br&gt;
Let’s cover some of the fundamental elements.&lt;/p&gt;
&lt;h4&gt;1. Labeling and Identifying Elements&lt;/h4&gt;
&lt;p&gt;The &lt;a href="https://developer.apple.com/documentation/swiftui/view/accessibilitylabel(_:)"&gt;.accessibility(label:)&lt;/a&gt; modifier help users with assistive technologies understand your UI.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;Text("Play")
    .accessibilityLabel(label: Text("Play button"))
    .accessibilityIdentifier("play_button")&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/accessibilityidentifier(_:)"&gt;.accessibilityIdentifier(_:)&lt;/a&gt; is useful for UI testing.&lt;br&gt;
&lt;br&gt;&lt;/p&gt;
&lt;h4&gt;2. Providing Hints&lt;/h4&gt;
&lt;p&gt;Hints can help users understand the purpose of interactive elements.&lt;br&gt;
The &lt;a href="https://developer.apple.com/documentation/swiftui/view/accessibilityhint(_:)"&gt;.accessibilityHint(_:)&lt;/a&gt; modifier provides additional information.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;Button(action: {
    // Action
}) {
    Image(systemName: "trash")
}
.accessibilityHint(Text("Deletes the current item"))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;h4&gt;3. Grouping Elements&lt;/h4&gt;
&lt;p&gt;Use the &lt;a href="https://developer.apple.com/documentation/swiftui/view/accessibilityelement(children:)"&gt;.accessibilityElement(children:)&lt;/a&gt; modifier to logically group UI elements together, improving navigation.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;VStack {
    Text("Name")
    TextField("Enter your name", text: $name)
}
.accessibilityElement(children: .combine)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;h4&gt;4. Hide non-essential Elements&lt;/h4&gt;
&lt;p&gt;Use the &lt;a href="https://developer.apple.com/documentation/swiftui/view/accessibilityhidden(_:)"&gt;.accessibilityHidden(_:)&lt;/a&gt; modifier to hide non-essential elements from assistive technologies.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;Text("I am here only for the fancy design!")
    .accessibilityHidden(true)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;h4&gt;5. Accessibility speach language&lt;/h4&gt;
&lt;p&gt;Use the &lt;a href="https://developer.apple.com/documentation/foundation/nsattributedstring/key/1620188-accessibilityspeechlanguage"&gt;.accessibilityLanguage&lt;/a&gt; modifier to set the language of the accessibility label.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;let attributedString = AttributedString(
    "Nederland is bekend om zijn tulpen en windmolens.",
    attributes: AttributeContainer([
        .accessibilitySpeechLanguage: "nl_NL"
    ])
)

Text(attributedString)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;h4&gt;6. Accessibility Actions&lt;/h4&gt;
&lt;p&gt;You can create custom actions that VoiceOver users can trigger using the &lt;a href="https://developer.apple.com/documentation/swiftui/view/accessibilityaction(named:_:)"&gt;.accessibilityAction(named:_:)&lt;/a&gt; modifier.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;Image(systemName: "heart")
    .accessibilityAction(named: Text("Like song")) {
        // Handle action
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;h4&gt;7. Accesibility Announcements&lt;/h4&gt;
&lt;p&gt;Use AccessibilityNotification to provide custom announcements for VoiceOver users.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;@State
private var isLoading = false

var body: some View {
    VStack {
        Button("Search catalog") {
            isLoading = true
        }

        if isLoading {
            ProgressView()
        }
    }
    // Track state changes
    .onChange(of: isLoading) { _, isLoading in
        if isLoading {
            UIAccessibility.post(
                notification: .announcement, 
                argument: "Search in progress"
            )
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;h4&gt;8. Accessibility focus state&lt;/h4&gt;
&lt;p&gt;Use the &lt;a href="https://developer.apple.com/documentation/swiftui/accessibilityfocusstate"&gt;@AccessibilityFocusState&lt;/a&gt; and &lt;a href="https://developer.apple.com/documentation/swiftui/view/accessibilityfocused(_:)"&gt;.accessibilityFocused(_:)&lt;/a&gt; modifier to manage the focus state of an element.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;@AccessibilityFocusState
private var isEmailFocused: Bool

@State
private var email = ""

var body: some View {
    Form {
        TextField("Email", text: $email, prompt: Text("Email"))
            // Binds the focus state of this TextField to the 'isEmailFocused' property.
            .accessibilityFocused($isEmailFocused)
            // The border color changes based on whether the TextField is focused:
            .border(isEmailFocused ? .red : .clear)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sample taken from: &lt;a href="https://appt.org/en/docs/swiftui/samples/accessibility-focus-indicator"&gt;https://appt.org/en/docs/swiftui/samples/accessibility-focus-indicator&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;
&lt;h4&gt;9. Accessibility sort priority&lt;/h4&gt;
&lt;p&gt;Use the &lt;a href="https://developer.apple.com/documentation/swiftui/view/accessibilitysortpriority(_:)"&gt;accessibilitySortPriority(_:)&lt;/a&gt; modifier to set the sort priority of an accessibility element.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;VStack {
    Text("First Element")
        .accessibilitySortPriority(2) // Reads second
    Text("Second Element")
        .accessibilitySortPriority(3) // Reads first
    Text("Third Element")
        .accessibilitySortPriority(1) // Reads third
}
.accessibilityElement(children: .contain) // Groups the stack's children&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;h4&gt;10. Accessibility traits&lt;/h4&gt;
&lt;p&gt;Use the &lt;a href="https://developer.apple.com/documentation/swiftui/view/accessibilityaddtraits(_:)"&gt;accessibilityAddTraits(_:)&lt;/a&gt; modifier to set the traits of an accessibility element.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;// Button Trait
Text("Tap Me")
    .accessibilityAddTraits(.isButton)

// Header Trait
Text("Section Header")
    .font(.headline)
    .accessibilityAddTraits(.isHeader)

// Link Trait
Text("Visit Website")
    .foregroundColor(.blue)
    .underline()
    .accessibilityAddTraits(.isLink)

// Image Trait
Image(systemName: "star.fill")
    .accessibilityAddTraits(.isImage)

// Custom Checkbox Trait
@State
private var isSelected: Bool = false

var body: some View {
    CustomCheckbox(isSelected: $isSelected)
        .accessibilityAddTraits(isSelected ? [.isSelected] : [])
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;h4&gt;11. Accessibility value&lt;/h4&gt;
&lt;p&gt;Use the &lt;a href="https://developer.apple.com/documentation/swiftui/view/accessibilityvalue(_:)"&gt;accessibilityValue(_:)&lt;/a&gt; modifier to set the value of an accessibility element.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;@State
private var progress: Double = 0

var body: some View {
    MySlider(value: $progress)
        .accessibilityValue("\(progress)")
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;h4&gt;12. Dynamic Type&lt;/h4&gt;
&lt;p&gt;Support Dynamic Type by using the font modifier with the .largeTitle, .title, .headline, .body, or .caption text styles.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;Text("Hello, World!")
    .font(.largeTitle)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;h4&gt;14. Autocomplete&lt;/h4&gt;
&lt;p&gt;Autocompletion is an accessibility feature that helps users complete text input.&lt;br&gt;
Use the &lt;a href="https://developer.apple.com/documentation/swiftui/view/searchsuggestions(_:)"&gt;.searchSuggestions(_:)&lt;/a&gt; modifier to provide search suggestions.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;ProductList()
    .searchable(text: $text)
    // Provide search suggestions
    .searchSuggestions {
        Text("🍎").searchCompletion("apple")
        Text("🍐").searchCompletion("pear")
        Text("🍌").searchCompletion("banana")
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;h4&gt;15. Dark Mode&lt;/h4&gt;
&lt;p&gt;Dark mode is an accessibility feature that helps users with light sensitivity or visual impairments.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;@Environment(\.colorScheme)
var colorScheme

var body: some View {
    HStack {
        if colorScheme == .dark {
            // Show moon icon (dark mode)
            Image(systemName: "moon.fill")
        } else {
            // Show sun icon (light mode)
            Image(systemName: "sun.max.fill")
        }

        Text(colorScheme == .dark ? "In dark mode" : "In light mode")
    }   
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;h3&gt;16. Sensory Feedback&lt;/h3&gt;
&lt;p&gt;Sensory feedback, like haptic feedback, can help users with visual impairments navigate your app.&lt;br&gt;
This can be achived using the &lt;a href="https://developer.apple.com/documentation/uikit/uiimpactfeedbackgenerator"&gt;UIImpactFeedbackGenerator&lt;/a&gt; and &lt;a href="https://developer.apple.com/documentation/uikit/uinotificationfeedbackgenerator"&gt;UINotificationFeedbackGenerator&lt;/a&gt; classes.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;Button("Tap Me for Haptic Feedback") {
    UIImpactFeedbackGenerator(style: .heavy)
        .impactOccurred()

    // Action
}

Button("Tap Me for Success") {
    UINotificationFeedbackGenerator()
        .notificationOccurred(.success)

    // Action
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;h3&gt;Testing Accessibility&lt;/h3&gt;
&lt;p&gt;Regularly test your app's accessibility using tools like VoiceOver.&lt;br&gt;
Navigate through your app and ensure that every element makes sense and that there are no barriers to interaction.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Accessibility is integral to creating inclusive, user-friendly apps. With SwiftUI's robust accessibility features, there's no excuse for not making your app usable by everyone. It's about designing with empathy and making sure your app stands out by being accessible to all.&lt;/p&gt;
&lt;h3&gt;Tip:&lt;/h3&gt;
&lt;p&gt;Use the &lt;strong&gt;Accessibility Inspector&lt;/strong&gt; in Xcode to check your app's accessibility, and enable and disable accessibility features to see how your app behaves.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view-accessibility/"&gt;Accessibility modifiers&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/accessibility/"&gt;Apple's Accessibility Documentation&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/design/human-interface-guidelines/accessibility#Text-display"&gt;Human Interface Guidelines: Accessibility&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Contact Provider Extension</title>    <link>https://wesleydegroot.nl/blog/contactprovider</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/contactprovider</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:12 +0200</pubDate>
    <category>ContactProvider</category>
    <category>Swift</category>
    <description>&lt;p&gt;The &lt;a href="https://developer.apple.com/documentation/contactprovider"&gt;ContactProvider&lt;/a&gt; framework lets your app create (business) contacts on the user's device.&lt;br&gt;
It is the prefered way to add contacts to the user's address book without interferring with their (personal) address book.&lt;/p&gt;
&lt;p&gt;In this post we will create a simple app that uses the &lt;code&gt;ContactProvider&lt;/code&gt; framework to add a business contact to the user's address book.&lt;/p&gt;
&lt;h2&gt;How are we going to setup our application?&lt;/h2&gt;
&lt;p&gt;We want these features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Adding a contact.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Reset the contacts database.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Sync to the contact provider extension (yes for this demo we'll do it manually).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We need a way to easily add and fetch contacts from our app's "database".&lt;br&gt;
We will create an observable object that will hold the contacts.&lt;/p&gt;
&lt;h2&gt;Required actions for the app and extension&lt;/h2&gt;
&lt;p&gt;In our app we need to do the following actions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;We need to tell the system that we are able to provide contacts.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;let manager = try ContactProviderManager()
try await manager.enable()&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We need to synchronize the contacts with the system.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;let manager = try ContactProviderManager()
try await manager.signalEnumerator()&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In our extension we need to conform to the &lt;code&gt;ContactItemEnumerator&lt;/code&gt; protocol.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/contactprovider/contactitemenumerator/enumeratecontent(in:for:)"&gt;enumeratecontent(in:for:)&lt;/a&gt; for syncing the initial contacts.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/contactprovider/contactitemenumerator/enumeratechanges(startingat:for:)"&gt;enumeratechanges(startingat:for:)&lt;/a&gt; for syncing the changes.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Building the app&lt;/h2&gt;
&lt;h3&gt;Add the &lt;code&gt;Contact Provider Extension&lt;/code&gt; in your app.&lt;/h3&gt;
&lt;p&gt;To enable &lt;a href="https://developer.apple.com/documentation/contactprovider"&gt;&lt;code&gt;ContactProvider&lt;/code&gt;&lt;/a&gt; in your app, you need to add the &lt;code&gt;Contact Provider Extension&lt;/code&gt; to your app.&lt;/p&gt;
&lt;h3&gt;Setup an App Group&lt;/h3&gt;
&lt;p&gt;To enable communication between your app and the &lt;code&gt;Contact Provider Extension&lt;/code&gt;, you need to setup an App Group.&lt;br&gt;
For this example we will use the &lt;code&gt;group.nl.wesleydegroot.contactproviderdemo&lt;/code&gt; App Group.&lt;/p&gt;
&lt;h3&gt;Building the main application&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import ContactProvider

struct ContentView: View {
    /// The database that holds the contacts.
    @ObservedObject
    var database = ContactItemDatabase()

    @State var firstName: String = ""
    @State var lastName: String = ""
    @State var email: String = ""
    @State var phoneNumber: String = ""

    /// The contact provider manager.
    let manager = try! ContactProviderManager()

    var body: some View {
        VStack {
            Button("Send to contact provider") {
                Task {
                    // Synchronise contacts
                    await synchroniseContacts() 
                }
            }
            .buttonStyle(.borderedProminent)
            Button("Reset Database") {
                Task {
                    // Reset the database
                    database.reset() 

                    // Reset all previously known contacts.
                    try? await manager.reset() 
                }
            }
            .buttonStyle(.borderedProminent)

            List {
                ForEach(database.contactItemsJSON, id: \.firstName) { item in
                    Text("\(item.firstName) \(item.lastName)")
                }
            }

            GroupBox {
                TextField("First name", text: $firstName)
                TextField("Last name", text: $lastName)
                TextField("Email", text: $email)
                TextField("Phone number", text: $phoneNumber)

                Button("Add") {
                    saveToDatabase()
                }
            }
            .task {
                randomContact()
            }
            .background(.secondary)
        }
        .padding()
        .task {
            do {
                // May prompt the person to enable the default domain.
                try await manager.enable()
            } catch {
                // Handle the error.
            }
        }
    }

    func saveToDatabase() {
        database.save(
            .init(
                firstName: firstName,
                lastName: lastName,
                phoneNumber: phoneNumber == "" ? nil : phoneNumber,
                emailAddress: email == "" ? nil : email
            )
        )

        Task {
            randomContact()
        }
    }

    func randomContact() {
        firstName = ["John", "Jane", "Wesley", "Alice", "Bob"].randomElement()!
            lastName = ["Doe", "Smith", "de Groot", "Johnson", "Williams"].randomElement()!
            email = "\(firstName.lowercased()).\(lastName.lowercased())@example.com"
            phoneNumber = "+31612345678"
    }

    func synchroniseContacts() async {
        do {
            try await manager.signalEnumerator()
        } catch {
            // Handle the error.
            print(error)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Building the "middleware" &lt;code&gt;ContactItemDatabase&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import Foundation
import Contacts
import ContactProvider

class ContactItemDatabase: ObservableObject {
    // The items that will be synchronised to the Contact Provider extension.
    @Published public var contactItems: [ContactItem] = []

    // The items that will be saved to the database.
    @Published public var contactItemsJSON: [ContactItemJSON] = []

    // The url of our shared database. (App Group)
    private let fileURL = FileManager.default.containerURL(
        // App Group identifier.
        forSecurityApplicationGroupIdentifier: "group.nl.wesleydegroot.contactproviderdemo"
    )?.appendingPathComponent("ContactItems").appendingPathExtension("json")

    // The JSON struct that will be saved to the database.
    struct ContactItemJSON: Codable, Hashable {
        /// The identifier of the contact.
        var identifier: String = UUID().uuidString

        /// The first name of the contact. (givenName)
        var firstName: String

        /// The last name of the contact.
        var lastName: String

        /// The phone number of the contact.
        var phoneNumber: String?

        /// The email address of the contact.
        var emailAddress: String?
    }

    init() {
        guard let fileURL else {
            print("Could not find file URL; is the App Group identifier correct?")
            return
        }

        // Load the contents of our json (database) file.
        if let data = try? Data(contentsOf: fileURL) {
            let decoder = JSONDecoder()
            // Decode the JSON data from our ContactItemJSON struct.
            if let decoded = try? decoder.decode([ContactItemJSON].self, from: data) {
                // Save the decoded data to our contactItemsJSON array.
                // And remove any duplicates Array(Set(...)).
                contactItemsJSON = Array(Set(decoded)) 

                // Walk trough our contacts
                for item in contactItemsJSON {
                    // Create a new contact.
                    let contact: CNMutableContact = .init()

                    // Set the first name of the contact.
                    contact.givenName = item.firstName

                    // Set the last name of the contact.
                    contact.familyName = item.lastName

                    // If the contact has a phone number, add it to the contact.
                    if let phoneNumber = item.phoneNumber {
                        contact.phoneNumbers = [
                            .init(
                                label: "Mobile",
                                value: CNPhoneNumber(stringValue: phoneNumber)
                            )
                        ]
                    }

                    // If the contact has an email address, add it to the contact.
                    if let emailAddress = item.emailAddress {
                        contact.emailAddresses = [
                            .init(
                                label: "Email",
                                value: emailAddress as NSString
                            )
                        ]
                    }

                    // Create a new contact item identifier, from the contact identifier.
                    let identifier: ContactItem.Identifier = .init(item.identifier)

                    // Add the contact to the contactItems array.
                    contactItems.append(
                        .contact(contact, identifier)
                    )
                }
            }
        }
    }

    /// Reset the database.
    func reset() {
        // Remove all `ContactItem` items.
        contactItems.removeAll()

        // Remove all `ContactItemJSON` items.
        contactItemsJSON.removeAll()

        // Save the empty array to the database.
        try? JSONEncoder().encode(contactItemsJSON).write(to: fileURL!)
    }

    func save(_ contactItem: ContactItemJSON) {
        // Append the new contact to the contactItemsJSON array.
        contactItemsJSON.append(contactItem)

        // Save the new contact to the database.
        try? JSONEncoder().encode(contactItemsJSON).write(to: fileURL!)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Building the ContactProvider Extension&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import Foundation
import ContactProvider
import Contacts
import OSLog

@main
class Provider: ContactProviderExtension {
    private let rootContainerEnumerator: ProviderRootContainerEnumerator

    required init() {
        rootContainerEnumerator = ProviderRootContainerEnumerator()
    }

    func configure(for domain: ContactProviderDomain) {
        rootContainerEnumerator.configure(for: domain)
    }

    func enumerator(for collection: ContactItem.Identifier) -&amp;gt; ContactItemEnumerator {
        return rootContainerEnumerator
    }

    func invalidate() async throws {
        // TODO: Stop any enumeration and cleanup as the extension will be terminated.
    }
}

class ProviderRootContainerEnumerator: ContactItemEnumerator {
    /// The database that holds the contacts.
    private let database = ContactItemDatabase()

    func configure(for domain: ContactProviderDomain) {
        // TODO: If needed, configure your enumerator for the domain.
    }

    func enumerateContent(in page: ContactItemPage, for observer: ContactItemContentObserver) async {
        // Send the current contacts to the observer.
        observer.didEnumerate(self.database.contactItems)

        // Signal that we are done with the enumeration.
        observer.didFinishEnumeratingContent(upTo: "&amp;lt;currentDatabaseGenerationMarker&amp;gt;".data(using: .utf8)!)
    }


    func enumerateChanges(startingAt syncAnchor: ContactItemSyncAnchor, for observer: ContactItemChangeObserver) async {
        // Send the changes to the observer.
        observer.didUpdate(self.database.contactItems)

        // In this demo we dont support "remove" altough it can be implemented by giving the identifiers for the deleted items
        // observer.didDelete(self.database.deletedItemIdentifiers)

        // Signal that we are done with the enumeration.
        observer.didFinishEnumeratingChanges(
            upTo: ContactItemSyncAnchor(generationMarker: "&amp;lt;lastChangeGenerationMarker&amp;gt;".data(using: .utf8)!, offset: 0),
            moreComing: false
        )
    }

    func invalidate() async {
        // TODO: Stop the enumeration and cleanup as the extension will be terminated.
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;You can download the full project from &lt;a href="https://github.com/0xWDG/ContactProviderDemo"&gt;https://github.com/0xWDG/ContactProviderDemo&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;ContactProvider is a great way to add contacts to the user's address book without interferring with their (personal) address book.&lt;br&gt;
The finish signals are a bit confusing, but once you get the hang of it, it is a great way to add contacts to the user's address book.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/contactprovider"&gt;ContactProvider&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/contactprovider/contactprovidermanager"&gt;ContactProviderManager&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/contactprovider/contactproviderextension"&gt;ContactProviderExtension&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/contactprovider/contactitemenumerator"&gt;ContactItemEnumerator&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/contactprovider/contactitemcontentobserver"&gt;ContactItemContentObserver&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/contactprovider/contactitemchangeobserver"&gt;ContactItemChangeObserver&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/contactprovider/contactitemsyncanchor"&gt;ContactItemSyncAnchor&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/0xWDG/ContactProviderDemo"&gt;https://github.com/0xWDG/ContactProviderDemo&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Using MetricKit</title>    <link>https://wesleydegroot.nl/blog/metrickit</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/metrickit</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:13 +0200</pubDate>
    <category>MetricKit</category>
    <category>Swift</category>
    <description>&lt;p&gt;In this post, I will show you how to use MetricKit to collect and analyze performance data from your app.&lt;/p&gt;
&lt;h2&gt;What is MetricKit?&lt;/h2&gt;
&lt;p&gt;MetricKit is a framework introduced in iOS 13 that allows you to collect and analyze performance data from your app.&lt;br&gt;
It provides a set of APIs to access metrics such as CPU usage, memory usage, disk I/O, and network I/O.&lt;br&gt;
You can use this data to identify performance bottlenecks, optimize your app, and improve the user experience.&lt;/p&gt;
&lt;h2&gt;What metrics can you collect?&lt;/h2&gt;
&lt;p&gt;MetricKit provides APIs to collect the following metrics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Diagnostics for crashes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Hangs&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Energy&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Disk writes&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;How to use MetricKit?&lt;/h2&gt;
&lt;p&gt;To use MetricKit in your app, you need to follow these steps:&lt;/p&gt;
&lt;h3&gt;Step 1: Import MetricKit&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import MetricKit&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Step 2: Register for metric updates&lt;/h3&gt;
&lt;p&gt;To receive metric updates, you need to create a class that conforms to the &lt;a href="https://developer.apple.com/documentation/metrickit/mxmetricmanagersubscriber"&gt;&lt;code&gt;MXMetricManagerSubscriber&lt;/code&gt;&lt;/a&gt; protocol and register your class with the &lt;a href="https://developer.apple.com/documentation/metrickit/mxmetricmanager/"&gt;&lt;code&gt;MXMetricManager&lt;/code&gt;&lt;/a&gt; singleton object in your app's main entry point.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;@main
struct DinnerConnectApp: App {
    private var metrics = MetricSubscriber()

    var body: some Scene {
        WindowGroup {
            MyAppView()
                .onAppear {
                    MXMetricManager.shared.add(metrics)
                }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Step 3: Implement the MetricSubscriber protocol&lt;/h3&gt;
&lt;p&gt;We create a MetricSubscriber class that conforms to the &lt;a href="https://developer.apple.com/documentation/metrickit/mxmetricmanagersubscriber"&gt;&lt;code&gt;MXMetricManagerSubscriber&lt;/code&gt;&lt;/a&gt; protocol to receive metric payloads.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;class MetricSubscriber: NSObject, MXMetricManagerSubscriber {
    func didReceive(_ payloads: [MXMetricPayload]) {
        print("didReceive MXMetricPayload")
        guard let firstPayload = payloads.first else { return }

        if let jsonString = String(
            data: firstPayload.jsonRepresentation(), 
            encoding: .utf8
        ) {
            print(jsonString)
        } else {
            print("Failed to encode")
        }
    }

    func didReceive(_ payloads: [MXDiagnosticPayload]) {
        print("didReceive MXDiagnosticPayload")
        guard let firstPayload = payloads.first else { return }

        if let jsonString = String(
            data: firstPayload.jsonRepresentation(), 
            encoding: .utf8
        ) {
            print(jsonString)
        } else {
            print("Failed to encode")
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Debugging MetricKit&lt;/h2&gt;
&lt;p&gt;MetricKit will only work on a real device, not on the simulator.&lt;br&gt;
To send a metric payload to your app, you can go to Debug &amp;gt; Simulate MetricKit Payloads in Xcode.&lt;/p&gt;
&lt;h4&gt;References/resources:&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/metrictkit"&gt;MetricKit&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/videos/play/wwdc2021/10258/"&gt;Understand and eliminate hangs from your app&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/videos/play/wwdc2019/417"&gt;Improving Battery Life and Performance&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>iOS App States</title>    <link>https://wesleydegroot.nl/blog/app-states-in-ios</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/app-states-in-ios</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:14 +0200</pubDate>
    <category>iOS</category>
    <category>App State</category>
    <description>&lt;p&gt;When you're developing an iOS app, it's important to understand the different states that your app can be in, to handle them correctly and provide a smooth user experience. In this post, we'll take a look at the different app states in iOS and how you can work with them.&lt;/p&gt;
&lt;h2&gt;Not Running&lt;/h2&gt;
&lt;p&gt;The app is not running. This is the initial state of the app when it's not launched or has been terminated by the system.&lt;/p&gt;
&lt;h2&gt;Inactive&lt;/h2&gt;
&lt;p&gt;The app is running in the foreground but is not receiving events. This can happen when the app is transitioning to a different state, such as when a phone call or SMS message is received, pr when Control Center or Notification Center is displayed.&lt;/p&gt;
&lt;h2&gt;Active&lt;/h2&gt;
&lt;p&gt;The app is running in the foreground and is receiving events. This is the normal state for the app when it's active and the user is interacting with it.&lt;/p&gt;
&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;The app is running in the background (not visible for the user) and performing background tasks. This can happen when the user switches to another app or when the app is suspended by the system.&lt;/p&gt;
&lt;h2&gt;Suspended&lt;/h2&gt;
&lt;p&gt;The app is in the background but is &lt;em&gt;not&lt;/em&gt; executing tasks. This is the state when the app is in the background but is not performing any tasks. The system can terminate the app in this state to free up resources.&lt;/p&gt;
&lt;p&gt;The OS can move the app to this state after it has been in the background for a while and is not performing any tasks.&lt;/p&gt;
&lt;h2&gt;Responding to App State Changes&lt;/h2&gt;
&lt;p&gt;You can respond to app state changes by adding the following modifiers to your SwiftUI views:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;    .onReceive(NotificationCenter.default.publisher(
        for: UIApplication.willResignActiveNotification
    )) { _ in
        // app is going to the background, so do something
    }
    .onReceive(NotificationCenter.default.publisher(
        for: UIApplication.willEnterForegroundNotification
    )) { _ in
        // app is coming to the foreground, so do something else
    }&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;.onAppear&lt;/code&gt; - Called when the view appears on the screen.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;.onDisappear&lt;/code&gt; - Called when the view disappears from the screen.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;.onReceive&lt;/code&gt; - Called when the view receives a notification.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;.onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification))&lt;/code&gt; - Called when the app is about to enter the foreground.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;.onReceive(NotificationCenter.default.publisher(for: UIApplication.didEnterBackgroundNotification))&lt;/code&gt; - Called when the app enters the background.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;.onReceive(NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification))&lt;/code&gt; - Called when the app is about to become inactive.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;.onReceive(NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification))&lt;/code&gt; - Called when the app becomes active.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;.onReceive(NotificationCenter.default.publisher(for: UIApplication.willTerminateNotification))&lt;/code&gt; - Called when the app is about to be terminated.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;.onReceive(NotificationCenter.default.publisher(for: UIApplication.didFinishLaunchingNotification))&lt;/code&gt; - Called when the app has finished launching.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;.onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification))&lt;/code&gt; - Called when the app is about to enter the foreground.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;.onReceive(NotificationCenter.default.publisher(for: UIApplication.didEnterBackgroundNotification))&lt;/code&gt; - Called when the app enters the background.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;.onReceive(NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification))&lt;/code&gt; - Called when the app is about to become inactive.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;.onReceive(NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification))&lt;/code&gt; - Called when the app becomes active.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle"&gt;https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Controllig login and background items</title>    <link>https://wesleydegroot.nl/blog/controlling-login-and-background-items</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/controlling-login-and-background-items</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:14 +0200</pubDate>
    <category>macOS</category>
    <category>background</category>
    <category>login</category>
    <description>&lt;p&gt;In This post, we'll take a look at how to control login and background items on macOS.&lt;br&gt;
When you start your Mac, you may have noticed that some apps or items automatically start up or appear in the background. This can be useful for apps that you use frequently, but it can also slow down your Mac or clutter your desktop.&lt;/p&gt;
&lt;h2&gt;Difference between Login Items and Background Items&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Login Items&lt;/strong&gt;: These are apps or items that automatically start up when you log in to your Mac. They can be useful for apps that you use frequently, such as messaging apps, email clients, or productivity tools.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Background Items&lt;/strong&gt;: These are apps or items that run in the background when you start your Mac. They can be useful for apps that need to perform tasks in the background, such as cloud storage services, backup utilities, or system utilities.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;* You can add or remove login items in the Users &amp;amp; Groups preferences pane in System Preferences. You can also use the &lt;code&gt;sfltool&lt;/code&gt; command-line tool to list, add, remove, or reset login items, this is the tool we will use in this post.&lt;/p&gt;
&lt;h2&gt;How to List Login Items&lt;/h2&gt;
&lt;p&gt;This command will list all the login items that are set to start up when you log in to your Mac.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Open terminal&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Type &lt;code&gt;sudo sfltool dumpbtm&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;* Tip: Export the output to a text file, so you can read the values if you have resetted the login items.&lt;/p&gt;
&lt;h2&gt;Dump Login Items to a Text File&lt;/h2&gt;
&lt;p&gt;This can be useful if you want to save the list of login items to a text file for reference before making any changes.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Open terminal&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Type &lt;code&gt;sudo sfltool dumpbtm &amp;gt; ~/Desktop.login_items.txt&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open the file &lt;code&gt;login_items.txt&lt;/code&gt; on your desktop to see the list of login items.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;How to Add Login Items&lt;/h2&gt;
&lt;p&gt;Adding login items to start those apps when you log in to your Mac.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Open terminal&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Type &lt;code&gt;sudo sfltool add --label "App Name" -a "/Applications/AppName.app"&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;How to Remove Login Items&lt;/h2&gt;
&lt;p&gt;Removing login items to stop those apps from starting when you log in to your Mac.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Open terminal&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Type &lt;code&gt;sudo sfltool remove --label "App Name"&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Reset Login Items&lt;/h2&gt;
&lt;p&gt;If you see a whole bunch of login items that you don't recognize or can't remove, you can reset the login items list to the default settings.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Open terminal&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Type &lt;code&gt;sudo sfltool reset&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&lt;code&gt;launchctl&lt;/code&gt; Command (Background Items)&lt;/h2&gt;
&lt;p&gt;You can use the &lt;code&gt;launchctl&lt;/code&gt; command-line tool to manage background items on macOS. Here are some useful commands:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;launchctl list&lt;/code&gt; - List all background items.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;launchctl start &amp;lt;label&amp;gt;&lt;/code&gt; - Start a background item.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;launchctl stop &amp;lt;label&amp;gt;&lt;/code&gt; - Stop a background item.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;launchctl remove &amp;lt;label&amp;gt;&lt;/code&gt; - Remove a background item.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;launchctl load &amp;lt;path&amp;gt;&lt;/code&gt; - Load a background item from a property list file.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;launchctl unload &amp;lt;path&amp;gt;&lt;/code&gt; - Unload a background item from a property list file.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;launchctl print &amp;lt;label&amp;gt;&lt;/code&gt; - Print the properties of a background item.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Items loaded by &lt;code&gt;launchctl&lt;/code&gt; are usually controlled by Property Lists installed in the &lt;code&gt;~/Library/LaunchAgents&lt;/code&gt; folder.&lt;/p&gt;
&lt;h2&gt;Example Launch Agent.&lt;/h2&gt;
&lt;p&gt;Here is an example of a Launc Agent file that imports screenshots i've taken into my photo library using Apples Shortcuts Application with &lt;a href="https://www.icloud.com/shortcuts/2c7c5cb4a4574437af66623b75521b16"&gt;this shortcut&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-xml"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;
&amp;lt;!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&amp;gt;
&amp;lt;plist version="1.0"&amp;gt;
  &amp;lt;dict&amp;gt;
     &amp;lt;key&amp;gt;Label&amp;lt;/key&amp;gt;
     &amp;lt;string&amp;gt;nl.wesleydegroot.ImportScreenshotsShortcut&amp;lt;/string&amp;gt;
     &amp;lt;key&amp;gt;ProgramArguments&amp;lt;/key&amp;gt;
     &amp;lt;array&amp;gt;
        &amp;lt;string&amp;gt;/usr/bin/shortcuts&amp;lt;/string&amp;gt;
        &amp;lt;string&amp;gt;run&amp;lt;/string&amp;gt;
        &amp;lt;string&amp;gt;Import Screenshots&amp;lt;/string&amp;gt;
     &amp;lt;/array&amp;gt;
     &amp;lt;key&amp;gt;RunAtLoad&amp;lt;/key&amp;gt;
     &amp;lt;true /&amp;gt;
     &amp;lt;key&amp;gt;StandardErrorPath&amp;lt;/key&amp;gt;
     &amp;lt;string&amp;gt;/dev/null&amp;lt;/string&amp;gt;
     &amp;lt;key&amp;gt;StandardOutPath&amp;lt;/key&amp;gt;
     &amp;lt;string&amp;gt;/dev/null&amp;lt;/string&amp;gt;
     &amp;lt;key&amp;gt;StartInterval&amp;lt;/key&amp;gt;
     &amp;lt;integer&amp;gt;30&amp;lt;/integer&amp;gt;
  &amp;lt;/dict&amp;gt;
&amp;lt;/plist&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This file is saved as &lt;code&gt;~/Library/LaunchAgents/nl.wesleydegroot.ImportScreenshotsShortcut.plist&lt;/code&gt; and is loaded using &lt;code&gt;launchctl load ~/Library/LaunchAgents/nl.wesleydegroot.ImportScreenshotsShortcut.plist&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Explanation of the keys in the Property List:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Label&lt;/code&gt;: A unique identifier for the item.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;ProgramArguments&lt;/code&gt;: The command to run when the item needs to be started.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;RunAtLoad&lt;/code&gt;: Whether the item should be started when the system starts.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;StandardErrorPath&lt;/code&gt;: The file to write standard error output to. (/dev/null means discard the output).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;StandardOutPath&lt;/code&gt;: The file to write standard output to. (/dev/null means discard the output).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;StartInterval&lt;/code&gt;: The interval at which the item should be started (in seconds).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Extra facts&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Background items are mostly controlled using Property Lists traditionally installed in LaunchAgents and LaunchDaemons folders.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You can use the &lt;code&gt;launchctl&lt;/code&gt; command-line tool to manage background items.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You can use the &lt;code&gt;Activity Monitor&lt;/code&gt; app to see which apps are running in the background and how much system resources they are using.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Exploring ControlGroup in SwiftUI</title>    <link>https://wesleydegroot.nl/blog/controlgroup-in-swiftui</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/controlgroup-in-swiftui</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:15 +0200</pubDate>
    <category>ControlGroup</category>
    <category>SwiftUI</category>
    <description>&lt;p&gt;In SwiftUI, a &lt;code&gt;ControlGroup&lt;/code&gt; provides a way to visually group controls together, making your UI more intuitive and structured. It helps you present related controls in a unified manner, enhancing the user experience by logically grouping similar actions.&lt;/p&gt;
&lt;h4&gt;Syntax Overview&lt;/h4&gt;
&lt;p&gt;Here's a basic example of how to use a &lt;code&gt;ControlGroup&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            ControlGroup {
                Button("Action 1") {
                    // Action 1 code
                }
                Button("Action 2") {
                    // Action 2 code
                }
            }
            .controlGroupStyle(.automatic)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/resources/controlgroup_automatic.png" alt="ControlGroup Automatic" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;In this example, we create a &lt;code&gt;ControlGroup&lt;/code&gt; inside a &lt;code&gt;VStack&lt;/code&gt;, containing two buttons. The &lt;code&gt;.controlGroupStyle(.automatic)&lt;/code&gt; modifier ensures that the group adopts the appropriate style based on the context it's used in.&lt;/p&gt;
&lt;h4&gt;Customizing &lt;code&gt;ControlGroup&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;While the automatic style is convenient, you can customize &lt;code&gt;ControlGroup&lt;/code&gt; to match your app's design. SwiftUI offers different styles, such as &lt;code&gt;navigation&lt;/code&gt; and &lt;code&gt;compact&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            ControlGroup {
                Button("Action 1") {
                    // Action 1 code
                }
                Button("Action 2") {
                    // Action 2 code
                }
            }
            .controlGroupStyle(.navigation)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/resources/controlgroup_navigation.png" alt="ControlGroup Navigation" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;In the example above, we use &lt;code&gt;.controlGroupStyle(.navigation)&lt;/code&gt; to give the control group a navigation-based appearance, suitable for toolbar items.&lt;/p&gt;
&lt;h4&gt;Practical Example&lt;/h4&gt;
&lt;p&gt;Consider an app where users frequently perform text formatting actions. You can use a &lt;code&gt;ControlGroup&lt;/code&gt; to group formatting buttons together, providing a clean and organized interface:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct FormattingView: View {
    var body: some View {
        VStack {
            Text("Formatted Text")
                .font(.title)

            ControlGroup {
                Button(action: boldText) {
                    Image(systemName: "bold")
                }
                Button(action: italicizeText) {
                    Image(systemName: "italic")
                }
                Button(action: underlineText) {
                    Image(systemName: "underline")
                }
            }
            .controlGroupStyle(.menu)
        }
    }

    func boldText() {
        // Bold text code
    }

    func italicizeText() {
        // Italicize text code
    }

    func underlineText() {
        // Underline text code
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;.menu&lt;/code&gt;:&lt;br&gt;
&lt;img src="/resources/controlgroup_menu.png" alt="ControlGroup menu" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.compactMenu&lt;/code&gt;&lt;br&gt;
&lt;img src="/resources/controlgroup_compactMenu.png" alt="ControlGroup compactMenu" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;In this &lt;code&gt;FormattingView&lt;/code&gt;, we group three formatting actions—bold, italic, and underline—using &lt;code&gt;ControlGroup&lt;/code&gt; with a &lt;code&gt;.menu&lt;/code&gt; / &lt;code&gt;.compactMenu&lt;/code&gt; style, making the actions easily accessible and visually appealing.&lt;/p&gt;
&lt;h4&gt;Interesing Use Cases&lt;/h4&gt;
&lt;p&gt;Control groups work great on (context) menu's, see the following example:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ContextMenuView: View {
    var body: some View {
        VStack {
            Text("Right-click (or long press) to show context menu")
                .contextMenu {
                    ControlGroup {
                        Button {
                             // Cut action
                        } label: {
                             Label("Cut", systemImage: "scissors")
                        }

                        Button {
                            // Copy action
                        } label: {
                            Label("Copy", systemImage: "doc.on.doc")
                        }

                        Button {
                            // Paste action
                        } label: {
                            Label("Paste", systemImage: "doc.on.clipboard")
                        }
                    }
                }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/resources/controlgroup_contextmenu.png" alt="ControlGroup in contextmenu" loading="lazy"&gt;&lt;/p&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;ControlGroup&lt;/code&gt; is a powerful feature in SwiftUI that enhances the organization of controls in your app. By logically grouping related actions, you can improve the user experience and create a more intuitive interface. Experiment with different control group styles to find the best fit for your app's design.&lt;/p&gt;</description>
  </item>
  <item>
    <title>Expressible Literals</title>    <link>https://wesleydegroot.nl/blog/expressible-literals</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/expressible-literals</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:15 +0200</pubDate>
    <category>Swift</category>
    <category>expressible-literals</category>
    <description>&lt;p&gt;Expressible Literals in Swift are a powerful feature that allows you to create custom types that can be initialized using literals.&lt;br&gt;
This post will show you how to create your own custom types that can be initialized using literals.&lt;/p&gt;
&lt;h2&gt;What is a literal?&lt;/h2&gt;
&lt;p&gt;A literal is a notation for representing a fixed value such as integers, strings, and booleans.&lt;br&gt;
Literals in Swift are made possible by several available protocols.&lt;br&gt;
Standard types conform to these protocols and allow us to initialize values as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-Swift"&gt;var integer = 0 // ExpressibleByIntegerLiteral
var string = "Hello!" // ExpressibleByStringLiteral
var array = [0, 1, 2] // ExpressibleByArrayLiteral
var dictionary = ["Key": "Value"] // ExpressibleByDictionaryLiteral
var boolean = true // ExpressibleByBooleanLiteral&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;How to create a custom type that can be initialized using literals&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-Swift"&gt;// ExpressibleBy..Literal protocol is used to create custom types that can be initialized using literals
extension CLASS: ExpressibleByStringLiteral {
    // Required initializer for ExpressibleBy...Literal
    public init(stringLiteral value: String) {
        // Initialize the class with the ... value
        self.init(value)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Example 1: Image&lt;/h2&gt;
&lt;p&gt;Before:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-Swift"&gt;import SwiftUI

let myImage = Image(named: "imageName")&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using ExpressibleByStringLiteral:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-Swift"&gt;import SwiftUI
extension Image: ExpressibleByStringLiteral {
    public init(stringLiteral value: String) {
        self.init(value)
    }
}

let myImage: Image = "imageName"&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Example 2: URL&lt;/h2&gt;
&lt;p&gt;Before:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-Swift"&gt;let myURL = URL(string: "https://wesleydegroot.nl")&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using ExpressibleByStringLiteral:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-Swift"&gt;extension URL: ExpressibleByStringLiteral {
    public init(stringLiteral value: String) {
        self.init(string: value)!
    }
}

let myURL: URL = "https://wesleydegroot.nl"&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Example 3: Date&lt;/h2&gt;
&lt;p&gt;Before:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-Swift"&gt;let formatter = DateFormatter()
formatter.dateFormat = "dd-MM-yyyy"
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)

let date = formatter.date(from: "03-08-1990")
print(date) // Prints: 1990-08-03 00:00:00 +0000&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using ExpressibleByStringLiteral:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-Swift"&gt;extension Date: ExpressibleByStringLiteral {
    public init(stringLiteral value: String) {
        let formatter = DateFormatter()
        formatter.dateFormat = "dd-MM-yyyy"
        formatter.locale = Locale(identifier: "en_US_POSIX")
        formatter.timeZone = TimeZone(secondsFromGMT: 0)
        self = formatter.date(from: value)!
    }
}

let date: Date = "03-08-1990"
print(date) // Prints: 1990-08-03 00:00:00 +0000&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Expressible Literals in Swift are a powerful feature that allows you to create custom types that can be initialized using literals.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swift/expressiblebystringliteral"&gt;https://developer.apple.com/documentation/swift/expressiblebystringliteral&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://docs.swift.org/swift-book/documentation/the-swift-programming-language/initialization/"&gt;https://docs.swift.org/swift-book/documentation/the-swift-programming-language/initialization/&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>JavaScriptCore</title>    <link>https://wesleydegroot.nl/blog/javascriptcore</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/javascriptcore</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:16 +0200</pubDate>
    <category>JavaScriptCore</category>
    <category>Swift</category>
    <description>&lt;p&gt;Whether you love it or hate it, JavaScript has become the most important language for developers in the world today. Yet despite any efforts we may take to change or replace it we’d be hard-pressed to deny its usefulness.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;JavaScriptCore&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;JavaScriptCore is a framework that provides a JavaScript engine for macOS and iOS. It allows developers to run JavaScript code within their applications. JavaScriptCore is a part of the WebKit framework and is used by Safari, Mail, and many other applications on macOS and iOS.&lt;/p&gt;
&lt;h2&gt;JavaScriptCore / Objective-C / Swift Types&lt;/h2&gt;
&lt;p&gt;JavaScriptCore uses a set of types to represent JavaScript values in Objective-C and Swift. These types are used to interact with JavaScript code and pass values between JavaScript and native code.&lt;br&gt;
Here is a table that shows the mapping between JavaScript types and Objective-C/Swift types:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;JavaScript Type&lt;/th&gt;
&lt;th&gt;JSValue method&lt;/th&gt;
&lt;th&gt;Objective-C Type&lt;/th&gt;
&lt;th&gt;Swift Type&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;toString&lt;/td&gt;
&lt;td&gt;NSString&lt;/td&gt;
&lt;td&gt;String!&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;boolean&lt;/td&gt;
&lt;td&gt;toBool&lt;/td&gt;
&lt;td&gt;BOOL&lt;/td&gt;
&lt;td&gt;Bool&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;number&lt;/td&gt;
&lt;td&gt;toNumber&lt;/td&gt;
&lt;td&gt;NSNumber&lt;/td&gt;
&lt;td&gt;NSNumber!&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;number&lt;/td&gt;
&lt;td&gt;toDouble&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;td&gt;Double&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;number&lt;/td&gt;
&lt;td&gt;toInt32&lt;/td&gt;
&lt;td&gt;int32_t&lt;/td&gt;
&lt;td&gt;Int32&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;number&lt;/td&gt;
&lt;td&gt;toUInt32&lt;/td&gt;
&lt;td&gt;uint32_t&lt;/td&gt;
&lt;td&gt;UInt32&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;date&lt;/td&gt;
&lt;td&gt;toDate&lt;/td&gt;
&lt;td&gt;NSDate&lt;/td&gt;
&lt;td&gt;Date?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;array&lt;/td&gt;
&lt;td&gt;toArray&lt;/td&gt;
&lt;td&gt;NSArray&lt;/td&gt;
&lt;td&gt;[Any]!&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;object&lt;/td&gt;
&lt;td&gt;toDictionary&lt;/td&gt;
&lt;td&gt;NSDictionary&lt;/td&gt;
&lt;td&gt;[AnyHashable: Any]!&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;class&lt;/td&gt;
&lt;td&gt;toObject&lt;/td&gt;
&lt;td&gt;custom type&lt;/td&gt;
&lt;td&gt;custom type&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Simple Example&lt;/h2&gt;
&lt;p&gt;Here’s a simple example that evaluates a JavaScript expression and prints the result:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import JavaScriptCore

let context = JSContext()!
let result = context.evaluateScript("1 + 2 + 3")
result?.toInt32() // 6&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Handling Exceptions&lt;/h2&gt;
&lt;p&gt;If a script throws an exception, JavaScriptCore calls the exception handler you provide. The handler receives the context and the exception object, which you can use to get the exception message.&lt;br&gt;
below is an example how to create an exception handler:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import JavaScriptCore

let context = JSContext()!
context.exceptionHandler = { context, exception in
    print(exception!.toString())
}

context.evaluateScript("**INVALID**")
// Prints "SyntaxError: Unexpected token '**'"&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: You can’t tell whether a script evaluated successfully based on its return value. For instance, both variable assignment and syntax errors produce undefined return values.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;(Hacky) Way to dertimine if a script was evaluated successfully&lt;/h2&gt;
&lt;p&gt;If we want to determine if a script was evaluated successfully, we can set a variable in the script and then check if that variable exists in the context.&lt;br&gt;
if the variable exists, the script was evaluated successfully.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import JavaScriptCore

let context = JSContext()!
let result = context.evaluateScript("1 + 2 + 3; var didRun = true;")
let didRun = context.objectForKeyedSubscript("didRun")?.toBool()&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Virtual Machines and Contexts&lt;/h2&gt;
&lt;p&gt;JavaScriptCore uses a virtual machine to manage memory and resources. A virtual machine can have multiple contexts, each of which is a separate JavaScript environment. Contexts are isolated from each other, so they can’t share variables or functions.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;let queue = DispatchQueue(label: "myVirtualMachineQueue")
let vm = queue.sync { JSVirtualMachine()! }
let context = JSContext(virtualMachine: vm)!&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Creating a function in JavaScript&lt;/h2&gt;
&lt;p&gt;JavaScript evaluation isn’t limited to single statements. When you evaluate code that declares a function or variable, it’s saved into the context’s object space.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;context.evaluateScript("""
function twoTimes(number) {
    return number * 2;
}
""")

context.evaluateScript("twoTimes(5)")?
       .toInt32() // 10&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Getting JavaScript Context Values from Swift&lt;/h2&gt;
&lt;p&gt;You can access named values from a JSContext by calling the &lt;code&gt;objectForKeyedSubscript(_:)&lt;/code&gt; method. This method returns a JSValue object that represents the value in the context.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;context.evaluateScript("var twoTimesFive = twoTimes(5)")

context.objectForKeyedSubscript("twoTimesFive")?
       .toInt32() // 10&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Setting Swift Values on a JavaScript Context&lt;/h2&gt;
&lt;p&gt;You can set a Swift value in a JSContext by calling the &lt;code&gt;setObject(_:forKeyedSubscript:)&lt;/code&gt; method. This method creates a new variable in the context with the given name and value.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;context.setObject(10, forKeyedSubscript: "swiftValue")

context.evaluateScript("swiftValue")?
       .toInt32() // 10&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Passing Functions between Swift and JavaScript&lt;/h2&gt;
&lt;p&gt;You can pass Swift functions to JavaScript by creating a JSValue object from a block. This object can be called from JavaScript as if it were a regular JavaScript function.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;let swiftFunction: @convention(block) (Int) -&amp;gt; Int = { 
    return $0 * 2
}
let jsFunction = JSValue(object: swiftFunction, in: context)
context.setObject(swiftFunction,
                  forKeyedSubscript: "swiftFunction" as NSString)

context.evaluateScript("swiftFunction(5)")?
       .toInt32() // 10

context.objectForKeyedSubscript("swiftFunction")?
       .call(withArguments: [6]) // 12&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Passing Swift Objects between Swift and JavaScript&lt;/h2&gt;
&lt;p&gt;To improve interoperability between language contexts, JavaScriptCore provides the JSExport protocol, which allows native classes to be mapped and initialized directly.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import JavaScriptCore

@objc protocol MyExport: JSExport {
    var value: Int { get set }
    func doubleValue() -&amp;gt; Int
    func doubleTheValue(value: Int) -&amp;gt; Int
}

class MyObject: NSObject, MyExport {
    var value: Int = 0

    func doubleValue() -&amp;gt; Int {
        return value * 2
    }

    func doubleTheValue(value: Int) -&amp;gt; Int {
        return value * 2
    }
}

let context = JSContext()!
context.setObject(
    MyObject.self, 
    forKeyedSubscript: "myObject" as NSString
)

context.evaluateScript("""
var obj = new myObject();
obj.value = 5;
obj.doubleValue(); // 10
""")

context.evaluateScript("""
myObject.doubleTheValue(5) // 10
""")&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;JavaScriptCore is a powerful framework that allows you to run JavaScript code within your applications.&lt;br&gt;
JavaScriptCore provides a simple API for evaluating JavaScript code, handling exceptions, creating functions, and passing values between Swift and JavaScript. By using JavaScriptCore, you can add dynamic scripting capabilities to your applications and create powerful, interactive user experiences.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/javascriptcore"&gt;https://developer.apple.com/documentation/javascriptcore&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/0xWDG/AuroraEditor/tree/development/AuroraEditor/Core/ExtensionSystem/Models/JavaScript%20Support"&gt;https://github.com/0xWDG/AuroraEditor/tree/development/AuroraEditor/Core/ExtensionSystem/Models/JavaScript%20Support&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>MacOS Terminal Commands</title>    <link>https://wesleydegroot.nl/blog/macos-terminal-commands</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/macos-terminal-commands</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:16 +0200</pubDate>
    <category>macOS</category>
    <category>Terminal</category>
    <description>&lt;p&gt;In this post i'll show you my favorite macOS terminal commands that i use often or find interesting.&lt;br&gt;
This blog post is a different kind of post than i usually write, but i hope you like it.&lt;/p&gt;
&lt;h2&gt;Enable/Disable beta updates&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Enroll developer seed&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo /System/Library/PrivateFrameworks/Seeding.framework/Versions/A/Resources/seedutil enroll DeveloperSeed&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enroll public seed&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo /System/Library/PrivateFrameworks/Seeding.framework/Versions/A/Resources/seedutil enroll PublicSeed&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unenroll seeds&lt;br&gt;(stop receiving beta updates)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo /System/Library/PrivateFrameworks/Seeding.framework/Versions/A/Resources/seedutil unenroll&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;* This command stopped working as of MacOS 13.4.&lt;/p&gt;
&lt;h2&gt;Xcode&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Debug/Internal Xcode&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults write com.apple.dt.Xcode ShowDVTDebugMenu -bool YES&lt;/code&gt;&lt;br&gt;&lt;code&gt;sudo mkdir -p /Applications/Xcode.app/Contents/Developer/AppleInternal/Library/Xcode/&lt;/code&gt;&lt;br&gt;&lt;code&gt;sudo touch /Applications/Xcode.app/Contents/Developer/AppleInternal/Library/Xcode/AppleInternal.plist&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Disable Xcode Cloud&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults write com.apple.dt.Xcode XcodeCloudUpsellPromptEnabled -bool false&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use 4 spaces for indentation&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults write com.apple.dt.Xcode DVTTextIndentWidth -int 4&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use spaces instead of tabs&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults write com.apple.dt.Xcode DVTTextIndentUsingTabs -bool NO&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Show the sidebar for indentation depth&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults write com.apple.dt.Xcode DVTTextShowFoldingSidebar -bool YES&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Show the full stack trace in the sidebar&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults write com.apple.dt.Xcode IDEAlwaysShowCompressedStackFrames -bool NO&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Show file extensions&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults write com.apple.dt.Xcode IDEFileExtensionDisplayMode -int 1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Show up to 30 lines of detail in the issue navigator&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults write com.apple.dt.Xcode IDEIssueNavigatorDetailLevel -int 30&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Show up to 30 lines of detail when searching&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults write com.apple.dt.Xcode IDESearchNavigatorDetailLevel -int 30&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Show the build number in the app icon&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults write com.apple.dt.Xcode DVTEnableDockIconVersionNumber -bool YES&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Does the above, and show a debug menu, but useless because of the above an the latter has more checks&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults write com.apple.dt.Xcode ShowDVTDebugMenu -bool YES&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Show details on indexing progress&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults write com.apple.dt.Xcode IDEIndexerActivityShowNumericProgress -bool true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Simulator&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Show touches simulator&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults write com.apple.iphonesimulator ShowSingleTouches 1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Finder&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Enable display of filename extensions&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults write NSGlobalDomain AppleShowAllExtensions -bool true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Disable the warning when changing a file extension&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults write com.apple.finder FXEnableExtensionChangeWarning -bool false&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Show hidden files by default&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults write com.apple.Finder AppleShowAllFiles -bool true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Disable smart quotes and smart dashes &lt;br&gt;they are annoying when typing code&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults write NSGlobalDomain NSAutomaticQuoteSubstitutionEnabled -bool false&lt;/code&gt;&lt;br&gt;&lt;code&gt;defaults write NSGlobalDomain NSAutomaticDashSubstitutionEnabled -bool false&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Open finder window in ~/Developer&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults write com.apple.finder NewWindowTargetPath -string "file://$HOME/Developer/"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Finder in column view&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults write com.apple.finder FXPreferredViewStyle Clmv&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Allow text selection in Quick Look&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults write com.apple.finder QLEnableTextSelection -bool TRUE&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Check for software updates daily, not just once per week&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults write com.apple.SoftwareUpdate ScheduleFrequency -int 1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Setting screenshots location to ~/Desktop/screenshots&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults write com.apple.screencapture location -string "$HOME/Desktop/screenshots"&lt;/code&gt;&lt;br&gt;I do this because i run a script that automatically imports screenshots to photos.&lt;br&gt;&lt;a href="http://wesleydegroot.nl/blog/Controlling-login-and-background-items"&gt;LaunchAgent&lt;/a&gt; and [The shortcut][&lt;a href="https://www.icloud.com/shortcuts/2c7c5cb4a4574437af66623b75521b16"&gt;https://www.icloud.com/shortcuts/2c7c5cb4a4574437af66623b75521b16&lt;/a&gt;]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Setting screenshot format to PNG&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults write com.apple.screencapture type -string "png"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Safari&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Show the full URL in the address bar&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults write com.apple.Safari ShowFullURLInSmartSearchField -bool true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enable the Develop menu and the Web Inspector&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults write com.apple.Safari IncludeInternalDebugMenu -bool true&lt;/code&gt;&lt;br&gt;&lt;code&gt;defaults write com.apple.Safari IncludeDevelopMenu -bool true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enabling Safari's bookmarks bar by default&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults write com.apple.Safari ShowFavoritesBar -bool true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enable the Develop menu and the Web Inspector in Safari&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults write com.apple.Safari IncludeInternalDebugMenu -bool true&lt;/code&gt;&lt;br&gt;&lt;code&gt;defaults write com.apple.Safari IncludeDevelopMenu -bool true&lt;/code&gt;&lt;br&gt;&lt;code&gt;defaults write com.apple.Safari WebKitDeveloperExtrasEnabledPreferenceKey -bool true&lt;/code&gt;&lt;br&gt;&lt;code&gt;defaults write com.apple.Safari com.apple.Safari.ContentPageGroupIdentifier.WebKit2DeveloperExtrasEnabled -bool true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Add a context menu item for showing the Web Inspector in web views&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults write NSGlobalDomain WebKitDeveloperExtras -bool true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Warn about fraudulent websites&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults write com.apple.Safari WarnAboutFraudulentWebsites -bool true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enable “Do Not Track”&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults write com.apple.Safari SendDoNotTrackHTTPHeader -bool true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Update extensions automatically&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults write com.apple.Safari InstallExtensionUpdatesAutomatically -bool true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Do not automatically open "safe" files after downloading&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults write com.apple.Safari AutoOpenSafeDownloads -bool false&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Photos&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Prevent Photos from opening automatically when devices are plugged in&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults -currentHost write com.apple.ImageCapture disableHotPlug -bool true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Mission Control&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Bottom left screen corner → Put to sleep&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults write com.apple.dock wvous-bl-corner -int 10&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bottom right corner → do nothing&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults write com.apple.dock wvous-bl-modifier -int 0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;(0: no-operation, 2: Mission Control, 3: Show application windows, 4: Desktop, 5: Start screen saver, 6: Disable screen saver, 7: Dashboard, 10: Put display to sleep, 11: Launchpad, 12: Notification Center, 13: Lock Screen)&lt;/p&gt;
&lt;h1&gt;AppKit&lt;/h1&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Show an internal AppKit debug menu&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaults write -g _NS_4445425547 -bool YES&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h1&gt;Terminal&lt;/h1&gt;
&lt;table&gt;
&lt;tr&gt;&lt;th&gt;Description&lt;/th&gt;&lt;th&gt;Command&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Making macOS Terminal use case-insensitive autocomplete&lt;/td&gt;&lt;td&gt;add &lt;pre&gt;&lt;code&gt;zstyle ':completion:*' matcher-list '' 'm:{a-zA-Z}={A-Za-z}' 'r:|=*' 'l:|=* r:|=*'
autoload -Uz compinit &amp;&amp; compinit&lt;/code&gt;&lt;/pre&gt; to your &lt;code&gt;~/.zshrc&lt;/code&gt; file. [from: &lt;a href="https://danielsaidi.com/blog/2025/01/16/making-macos-terminal-use-case-insensitive-autocomplete"&gt;Daniel Saidi&lt;/a&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</description>
  </item>
  <item>
    <title>NavigationStack</title>    <link>https://wesleydegroot.nl/blog/navigationstack</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/navigationstack</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:17 +0200</pubDate>
    <category>NavigationStack</category>
    <category>Swift</category>
    <description>&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/navigationstack"&gt;&lt;code&gt;NavigationStack&lt;/code&gt;&lt;/a&gt; is a SwiftUI view that provides a navigation stack for macOS and iOS.&lt;br&gt;
Navigation was done using &lt;code&gt;NavigationView&lt;/code&gt;, but it was deprecated in iOS 16 and was split into two new containers, &lt;code&gt;NavigationStack&lt;/code&gt; and &lt;code&gt;NavigationSplitView&lt;/code&gt;, and each one of them has new features.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;NavigationStack&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;NavigationStack is a way to manage navigation within a SwiftUI application.&lt;br&gt;
It provides a stack-based navigation system that allows you to push and pop views onto and off of the navigation stack.&lt;br&gt;
This makes it easy to create complex navigation flows in your application.&lt;/p&gt;
&lt;h2&gt;Requirements&lt;/h2&gt;
&lt;p&gt;iOS 16.0+, iPadOS 16.0+, Mac Catalyst 16.0+, macOS 13.0+, tvOS 16.0+, visionOS 1.0+, watchOS 9.0+&lt;/p&gt;
&lt;h2&gt;Key Features of NavigationStack&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Declarative Syntax&lt;/strong&gt;: Like all SwiftUI components, &lt;code&gt;NavigationStack&lt;/code&gt; uses a declarative syntax, making it easy to define the navigation flow in your app.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;State Management&lt;/strong&gt;: It integrates seamlessly with SwiftUI's state management, allowing you to control the navigation stack based on your app's state.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Custom Transitions&lt;/strong&gt;: You can customize the transitions between views, providing a more engaging user experience.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Deep Linking&lt;/strong&gt;: &lt;code&gt;NavigationStack&lt;/code&gt; supports deep linking, enabling users to navigate directly to specific views within your app.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Navigate to a view&lt;/h2&gt;
&lt;p&gt;With &lt;code&gt;NavigationStack&lt;/code&gt;, you can navigate to a view by appending a view to the navigation path.&lt;br&gt;
This can be done programmatically by calling &lt;code&gt;append&lt;/code&gt; on the &lt;code&gt;NavigationPath&lt;/code&gt; object, or by using a &lt;code&gt;Button&lt;/code&gt; or &lt;code&gt;NavigationLink&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;Button("Go to profile") {
    navPath.append("profile") // This will trigger navigationDestination(for: String.self)
    // You can also route it to a specific view by using a specific identifier like "profile/1" (see example 3)
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Example 1&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ContentView: View {
    @State
    private var navigationPath = NavigationPath()

    var body: some View {
        // For sake of this demo the button is directly in the navigation stack
        NavigationStack(path: $navigationPath) {
            Button("Open view with identifier qwerty") {
               navigationPath.append("qwerty") // This will trigger navigationDestination(for: String.self)
               // if you use a Int, it needs to be Int.self, and so on.
            }
            .navigationDestination(for: String.self) { identifier in
                YourView(withIdentifier: identifier)
            }
        }
    }
}

struct YourView: View {
    var identifier: String

    var body: some View {
        Text("Hello, \(identifier)!")
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Example 2 with pop to root&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ContentView: View {
    @State
    private var navigationPath = NavigationPath()

    var body: some View {
        // For sake of this demo the button is directly in the navigation stack
        NavigationStack(path: $navigationPath) {
            Button("Open view with identifier qwerty") {
               navigationPath.append("qwerty")
            }
            .navigationDestination(for: String.self) { identifier in
                VStack { 
                    Text("Hello, \(identifier)!")

                    var random = Int.random(in: 0...100)
                    Button("Add view \(random)") {
                        navigationPath.append("Random View \(random)")
                    }
                    Button("Back") {
                        navigationPath.removeLast()
                    }
                    Button("Root View") {
                        navigationPath = NavigationPath()
                    }
                }
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Example Navigate to a view with a specific identifier&lt;/h2&gt;
&lt;p&gt;You can also navigate to a view with a specific identifier by appending the identifier to the navigation path.&lt;br&gt;
This can be done programmatically by calling &lt;code&gt;append&lt;/code&gt; on the &lt;code&gt;NavigationPath&lt;/code&gt; object, or by using a &lt;code&gt;Button&lt;/code&gt; or &lt;code&gt;NavigationLink&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;
import SwiftUI

struct ContentView: View {
    @State
    private var navigationPath = NavigationPath()

    var body: some View {
        // For sake of this demo the button is directly in the navigation stack
        NavigationStack(path: $navigationPath) {
            BButton("Go to profile") {
                navigationPath.append("profile/1") // This will trigger navigationDestination(for: String.self)
            }
            .navigationDestination(for: String.self) { identifier in
                // Note: this is a simple example, in a real app you would probably want to use a switch statement or something similar to handle different identifiers.
                if identifier.hasPrefix("profile/") {
                    let profileID = identifier.replacingOccurrences(of: "profile/", with: "")
                    ProfileView(profileID: profileID)
                }
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;NavigationStack is a powerful tool for managing navigation in your SwiftUI app, especially when you need to use routing and other advanced navigation features.&lt;/p&gt;
&lt;h2&gt;Credits&lt;/h2&gt;
&lt;p&gt;I created this article for a &lt;a href="https://linkedin.com/in/theopalios"&gt;friend&lt;/a&gt; to help him to navigate in his &lt;a href="https://www.unicornlabs.nl/"&gt;Alzheimer’s Detection App&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/navigationstack"&gt;https://developer.apple.com/documentation/swiftui/navigationstack&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/navigationpath"&gt;https://developer.apple.com/documentation/swiftui/navigationpath&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/navigationdestination(for:destination"&gt;https://developer.apple.com/documentation/swiftui/view/navigationdestination(for:destination&lt;/a&gt;:)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/navigationlink"&gt;https://developer.apple.com/documentation/swiftui/navigationlink&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/button"&gt;https://developer.apple.com/documentation/swiftui/button&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Build a personal brand as developer</title>    <link>https://wesleydegroot.nl/blog/build-a-personal-brand-as-developer</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/build-a-personal-brand-as-developer</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:18 +0200</pubDate>
    <category>Developer</category>
    <category>Brand</category>
    <description>&lt;p&gt;Here are some tips to build a personal brand as a developer.&lt;/p&gt;
&lt;h2&gt;Define your brand&lt;/h2&gt;
&lt;p&gt;Start by defining your brand. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Who do you target.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;What are your strengths and skills.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;What are your values.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Where do you stand for.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;What are your goals.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This will help you to create a consistent message and image.&lt;/p&gt;
&lt;h2&gt;Create an Online Presence&lt;/h2&gt;
&lt;h4&gt;Create a portfolio&lt;/h4&gt;
&lt;p&gt;Build a professional website to showcase your projects, skills, and experiences.&lt;br&gt;
It's the first place where potential employers or collaborators will look to understand what you can do.&lt;br&gt;
Make sure it's clean, easy to navigate, and clearly highlights your best work.&lt;/p&gt;
&lt;h4&gt;(Optional) Create a blog&lt;/h4&gt;
&lt;p&gt;If you start a blog, you can share your thoughts, experiences, and knowledge with others.&lt;br&gt;
It is also a great way to improve your experience on a certain topic, to write the blog post you need to understand the topic well, this is a win-win situation because you are helping others and you are helping yourself.&lt;/p&gt;
&lt;h4&gt;Be active on social media&lt;/h4&gt;
&lt;p&gt;Use platforms like Twitter, Mastodon, Threads, LinkedIn, and GitHub to share your projects, engage with the (developer) community, and build your network. Regularly post updates, share interesting articles, and don't be afraid to showcase your personality. People connect with people, not just code.&lt;/p&gt;
&lt;h2&gt;Share Content&lt;/h2&gt;
&lt;p&gt;Write blog posts, create tutorial videos, and contribute to open-source projects.&lt;br&gt;
It's a great way to showcase your skills, share your knowledge, and build your reputation.&lt;/p&gt;
&lt;h2&gt;Network and Collaborate&lt;/h2&gt;
&lt;p&gt;Attend meetups, conferences, and hackathons to meet other developers, learn new things, and build relationships.&lt;/p&gt;
&lt;h2&gt;Brand Yourself&lt;/h2&gt;
&lt;p&gt;Create a logo, choose a color scheme, and use consistent fonts and styles across your website, social media profiles, and other online platforms.&lt;/p&gt;
&lt;h2&gt;Be yourself&lt;/h2&gt;
&lt;p&gt;Be true to yourself and your values, and don't try to be someone you're not.&lt;br&gt;
Authenticity is key to building a strong personal brand.&lt;/p&gt;
&lt;h2&gt;Dare to ask for help&lt;/h2&gt;
&lt;p&gt;Don't be afraid to ask for help, advice, or feedback.&lt;br&gt;
No one knows everything, asking for help is a sign of strength, not weakness.&lt;/p&gt;</description>
  </item>
  <item>
    <title>AppDevCon 2025</title>    <link>https://wesleydegroot.nl/blog/appdevcon-2025</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/appdevcon-2025</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:18 +0200</pubDate>
    <category>AppDevCon</category>
    <category>Conference</category>
    <description>&lt;p&gt;AppDevCon is a conference for application developers, focusing on the latest trends and technologies in app development.&lt;/p&gt;
&lt;p&gt;Below is a brief overview of some of the talks at the conference, with a small description of their talks.&lt;/p&gt;
&lt;h2&gt;&lt;a href="https://www.linkedin.com/in/antonio-markotic/"&gt;ANTONIO MARKOTIĆ&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;CUT COSTS, NOT CORNERS – MASTER MODULARIZATION WITH SPM&lt;/h3&gt;
&lt;p&gt;In this talk, we will go through 4 chapters (origins, maze, metamorphosis, legacy) I faced when I refactored our Monolith iOS project using SPM and afterward – alongside my team members – figured a way to revolutionize the way we integrate Kotlin Multiplatform code with iOS code. And the craziest part – no feature development was put on hold along this process. That’s kind of impressive, but I lied about it being the craziest part. The results we achieved are even crazier:&lt;/p&gt;
&lt;p&gt;55% reduction in local clean build time&lt;br&gt;
43% reduction in local incremental build time&lt;br&gt;
73% reduction in CI/CD build time.&lt;/p&gt;
&lt;p&gt;You will walk away with specific steps on how to modularize your Monolithic project with SPM, which traps to avoid, and how to maintain it moving forward. And if you are using KMM in your iOS project, you might just find out how to drastically improve the integration of it.&lt;/p&gt;
&lt;p&gt;Whether you’re an engineer, a team lead, or a CTO, this talk promises not only to enlighten but also to inspire your next big move.&lt;/p&gt;
&lt;p&gt;Video: &lt;a href="https://vimeo.com/1097833727?fl=pl&amp;amp;fe=vl"&gt;https://vimeo.com/1097833727?fl=pl&amp;amp;fe=vl&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;&lt;a href="https://www.linkedin.com/in/bruno-scheele-2a08aa295/"&gt;BRUNO SCHEELE&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;GETTING YOUR APP READY FOR EU’S ACCESSIBILITY LAWS&lt;/h3&gt;
&lt;p&gt;Allowing everyone to be able to use your app without compromises is becoming increasingly important, for inclusivity, market and legal reasons. Especially with the new European laws about app accessibility taking effect in 2025, you may be scrambling to get your app in order.&lt;/p&gt;
&lt;p&gt;In this talk, I explain what the European app accessibility laws mean in practical terms, how an organization can get started on making their apps comply and will illustrate some minor and major pitfalls they may encounter along the way. My goal is to help organizations get started on improving their apps’ accessibility, so that no one has to feel left behind while using them.&lt;/p&gt;
&lt;p&gt;Video: &lt;a href="https://vimeo.com/1097839086?fl=pl&amp;amp;fe=vl"&gt;https://vimeo.com/1097839086?fl=pl&amp;amp;fe=vl&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;&lt;a href="https://www.linkedin.com/in/tomlokhorst/"&gt;TOM LOKHORST&lt;/a&gt; &amp;amp; &lt;a href="https://www.linkedin.com/in/mathijskadijk/"&gt;MATHIJS KADIJK&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;FROM FRAMES TO FILES – BUILDING VIDEO WITH SWIFT&lt;/h3&gt;
&lt;p&gt;We use video everyday, but have you ever wondered how it actually works? How can we turn a bunch of pixels into moving pictures that we can see on screen, store in a file or transmit over the network? In this talk we’ll take a look at the history of video and build our own video from the ground up using only Swift. Then we’ll turn big uncompressed images, into small useable files using different Apple APIs.&lt;/p&gt;
&lt;p&gt;Video: &lt;a href="https://vimeo.com/1097833348?fl=pl&amp;amp;fe=vl"&gt;https://vimeo.com/1097833348?fl=pl&amp;amp;fe=vl&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;&lt;a href="https://www.linkedin.com/in/rob-whitaker/"&gt;ROB WHITAKER&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;TOUCH-FREE TOUCH SCREENS&lt;/h3&gt;
&lt;p&gt;iOS devices are the pinnacle of touch screen technology. Capacitive multi-touch on the original iPhone was what really set the device apart from anything that had come before and helped make the iPhone the most successful consumer product ever made. But touch isn’t the only way to interact with iOS. Keyboards, Mice, your voice, even moving your eyes, offer your users options to make their device and your apps work for them. And for some people with limb differences or motor disabilities these options are essential. A third of people with a physical disability report they are unable to complete basic digital tasks, more than 3 times that of people without a disability.&lt;br&gt;
So how do people who don’t have the dexterity required to use a touch screen interact with a device where touch is the primary input? And what simple changes can we make as app developers to remove barriers for them? This talk will give you the tools to build and test touch screen apps for non-touch users.&lt;/p&gt;
&lt;p&gt;Video: &lt;a href="https://vimeo.com/1097832577?fl=pl&amp;amp;fe=vl"&gt;https://vimeo.com/1097832577?fl=pl&amp;amp;fe=vl&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Fun Fact.&lt;/h2&gt;
&lt;p&gt;&lt;a href="/blog/Swift-Package-GameControllerKit"&gt;GameControllerKit&lt;/a&gt; is mentioned in the talk of Rob Whitaker!&lt;/p&gt;
&lt;h2&gt;View the schedule of 2025 here&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://appdevcon.nl/schedule-2025/"&gt;https://appdevcon.nl/schedule-2025/&lt;/a&gt;&lt;/p&gt;</description>
  </item>
  <item>
    <title>Fixing slow scrolling in Calendo</title>    <link>https://wesleydegroot.nl/blog/fixing-slow-scrolling-in-calendo</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/fixing-slow-scrolling-in-calendo</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:19 +0200</pubDate>
    <category>Calendo</category>
    <category>SwiftUI</category>
    <description>&lt;p&gt;In this post, I'll go through the steps which i took to fix a bug with slow scrolling in &lt;a href="/apps/Calendo"&gt;Calendo&lt;/a&gt;.&lt;br&gt;
Interested in the development of Calendo? Read my previous post about &lt;a href="/blog/Building-Calendo"&gt;Building Calendo&lt;/a&gt; or search on the &lt;a href="/blog/tag/Calendo"&gt;Tag: Calendo&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;What was the problem?&lt;/h2&gt;
&lt;p&gt;When you scrolled the calendar in Calendo, the scrolling was slow and laggy.&lt;br&gt;
This was a problem because it made the app feel unresponsive and slow.&lt;/p&gt;
&lt;p&gt;In this post, I explain how I fixed this problem and which steps I have taken to fix this.&lt;/p&gt;
&lt;h2&gt;Debugging... (pre-release)&lt;/h2&gt;
&lt;h4&gt;first suspect: The ScrollView&lt;/h4&gt;
&lt;p&gt;The ScrollView contains a list with appointments (-1 month to +1 month) from the currently selected month.&lt;br&gt;
The cells where build like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-plain"&gt;┌──────────────────────────────────┐
│ Date string              ┌─────┐ │
│ Appointment title        │ MAP │ │
│ Appointment Address      └─────┘ │
└──────────────────────────────────┘&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I was thinking that the MapView was causing the issue, because it was loading the map every time the cell was shown.&lt;/p&gt;
&lt;h4&gt;second suspect: The CalendarView&lt;/h4&gt;
&lt;p&gt;Is the CalendarView causing the issue?&lt;br&gt;
The CalendarView is pretty easy, it's just a grid with the days of the month, and a small circle that indicates the amount of appointments on that day.&lt;br&gt;
This can't be the issue.&lt;/p&gt;
&lt;h2&gt;Partial solution (pre-release)&lt;/h2&gt;
&lt;p&gt;I ended up removing the MapView, to increase the speed on older devices, it did not had the magical effect that I was hoping for, but at that moment I was thinking, every little bit helps.&lt;/p&gt;
&lt;p&gt;The New Appointment cell looked like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-plain"&gt;┌──────────────────────────────────┐
│ Date string                      │
│ Appointment title                │
│ Appointment Address              │
└──────────────────────────────────┘&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Debugging...&lt;/h2&gt;
&lt;p&gt;Or actually thinking, because I had no points to investigate, or maybe a better explanation, I had no idea where to start.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Too much appointments?&lt;br&gt;
Could the issue be that there are too many appointments in the calendar?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Is the day indicator causing the issue?&lt;br&gt;
The day indicator is a small circle that shows the current day in the calendar. Could this be causing the issue?&lt;br&gt;
If the day indicator is hidden, scrolling is snappy and fast, this is what made me think that the day indicator was causing the issue.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Problem avoiding&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Rebuilt CalendarView&lt;br&gt;
This improved the performance a little bit, but not enough to make it feel snappy.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Added a setting to disable certain calendars.&lt;br&gt;
This made loading faster if you had a lot of calendars, but it did not solve the issue.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Added a setting to disable the day indicator.&lt;br&gt;
This made the scrolling snappy and fast, but it was not a solution, because the day indicator is a key feature in the calendar.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Bonus find&lt;/h2&gt;
&lt;p&gt;Sometimes &lt;code&gt;EKEventStore&lt;/code&gt; was complaining that i was running to much connections to the calendar.&lt;br&gt;
After some investigation, I found out that I was constantly re-initializing the &lt;code&gt;EKEventStore&lt;/code&gt; (when Mocking the calendar), I fixed this to make a global variable to store the &lt;code&gt;EKEventStore&lt;/code&gt; in memory.&lt;/p&gt;
&lt;p&gt;This was as simple as:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;/// Global event store
public let globalEventStore = EKEventStore()&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;More Debugging...&lt;/h2&gt;
&lt;h4&gt;first suspect: The ScrollView&lt;/h4&gt;
&lt;p&gt;I tried to fire notifications instead of updating the &lt;code&gt;@State&lt;/code&gt;/&lt;code&gt;@EnvironmentObject&lt;/code&gt; in the &lt;code&gt;ScrollView&lt;/code&gt; cells, this did not solve the issue.    &lt;/p&gt;
&lt;h4&gt;second suspect: The CalendarView&lt;/h4&gt;
&lt;p&gt;I was too focused on the day indicator (I did not see the real issue)&lt;/p&gt;
&lt;h4&gt;third suspect: The CalendarView day indicator&lt;/h4&gt;
&lt;p&gt;I was thinking that the day indicator was causing the issue, because when I disabled the day indicator, the scrolling was fast and snappy.&lt;br&gt;
Below the day(number) in the month, there is a small circle that indicates the amount of appointments on that day.&lt;/p&gt;
&lt;p&gt;For debugging purposes, i was thinking maybe the issue is that I look up the appointments on the main thread, so i added this little piece of code to the &lt;code&gt;CalendarView&lt;/code&gt;'s &lt;code&gt;cell&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;extension Date {
    var appointments: [Color] {
        let eventStore = globalEventStore
        var events: [Color] = []

// DEBUG CODE START
#if targetEnvironment(simulator) 
        for _ in 0...Int.random(in: 0...3) {
            events.append(
                Color.init(cgColor: Color.random.cgColor!)
            )
        }
        return events
#endif
// DEBUG CODE END

        /// Some code to get the appointments
        return events
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Something strange happend, every time I scrolled the calendar, the colors where changing, this was not what I was expecting, because the appointments should be the same every time I scroll the calendar.&lt;/p&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;var appointmentStore: [Date: [Color]] = [:]

extension Date {
    var appointments: [Color] {
        if let appointments = appointmentStore[self] {
            return appointments
        }

        let eventStore = globalEventStore
        var events: [Color] = []

        /// Some code to get the appointments
        appointmentStore[self] = events
        return events
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The solution was to cache the appointments for each day in the month, this made the scrolling fast and snappy.&lt;br&gt;
I learned that you should not focus on one thing, but look at the bigger picture, and try to find the root cause of the issue.&lt;br&gt;
I stared myself blind on the day indicator, but the real issue was the appointments that where loaded every time the cell was shown.&lt;/p&gt;
&lt;p&gt;I hope this post helps you to find the root cause of your issue, and that you can fix it as well.&lt;/p&gt;
&lt;h3&gt;Download Calendo&lt;/h3&gt;
&lt;p&gt;You can download Calendo from the &lt;a class='button' href='https://apps.apple.com/us/app/calendo/id6651862759' target='_blank' class='button'&gt;&lt;i class="fa-brands fa-app-store"&gt;&lt;/i&gt; AppStore&lt;/a&gt;!.&lt;/p&gt;</description>
  </item>
  <item>
    <title>Dutch.swift</title>    <link>https://wesleydegroot.nl/blog/dutch.swift</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/dutch.swift</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:19 +0200</pubDate>
    <category>Swift</category>
    <description>&lt;p&gt;For april fools day, I wanted to write a Swift library that allows you to write Swift code in Dutch. It's called &lt;a href="https://github.com/0xWDG/Dutch.swift"&gt;Dutch.swift&lt;/a&gt; and it's available on GitHub. Unfortunately it didn't work out in the way I wanted it to, but I still wanted to share it with you.&lt;br&gt;
There is a way to get the idea i had in mind to work, but it required me to write a transpiler which converted the dutch code to swift code. This was not what I had in mind, so I decided to not go that route.&lt;/p&gt;
&lt;p&gt;I still want to share this with you:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import Dutch

let tekst: Tekst = "Hallo, Wereld!"

if true == waar {
    print(tekst)
}

zolang(true) { // while(true), Unfortunatly this needs parentheses
    print(tekst)
}

als(waar) { // if(waar), Unfortunatly this needs parentheses
    print(tekst)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I hope you like it! If you have any questions, feel free to ask me on the socials.&lt;/p&gt;
&lt;p&gt;🦋 &lt;a href="https://bsky.app/profile/0xWDG.bsky.social"&gt;@0xWDG&lt;/a&gt; 🐘 &lt;a href="https://mastodon.social/@0xWDG"&gt;mastodon.social/@0xWDG&lt;/a&gt; 🐦 &lt;a href="https://x.com/0xWDG"&gt;@0xWDG&lt;/a&gt; 🧵 &lt;a href="https://www.threads.net/@0xWDG"&gt;@0xWDG&lt;/a&gt; 🌐 &lt;a href="https://wesleydegroot.nl"&gt;wesleydegroot.nl&lt;/a&gt; 🤖 &lt;a href="https://discordapp.com/users/918438083861573692"&gt;Discord&lt;/a&gt;&lt;/p&gt;</description>
  </item>
  <item>
    <title>Building iWebTools</title>    <link>https://wesleydegroot.nl/blog/building-iwebtools</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/building-iwebtools</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:20 +0200</pubDate>
    <category>iWebTools</category>
    <description>&lt;p&gt;In this blog post I will show you how I build iWebTools.&lt;br&gt;
iWebTools is a tool to check websites and use some tools like a JWT Decoder and a crontab converter.&lt;/p&gt;
&lt;h2&gt;Basic Design&lt;/h2&gt;
&lt;p&gt;I started with a basic design, I'm not really a designer so I tend to create simple designs.&lt;/p&gt;
&lt;h3&gt;Check Websites tab&lt;/h3&gt;
&lt;p&gt;For the check websites tab I wanted to have a list of websites with their status.&lt;br&gt;
The status should be a colored dot, green for online and red for offline.&lt;br&gt;
The user should be able to add a website to the list.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Design&lt;/th&gt;&lt;th&gt;Result&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;
&lt;tr&gt;&lt;td&gt;&lt;pre&gt;&lt;code class="language-plaintext"&gt;
         Check Websites      [+]
┌──────────────────────────────┐
| https://mysite.com     [add] |
├──────────────────────────────┤
| wesleydegroot.nl        () ❯ |
| 123.123.123.123    status^   |
├──────────────────────────────┤
| wdgwv.com               () ❯ |
| 123.123.123.123    status^   |
├──────────────────────────────┤
| apple.com               () ❯ |
| 123.123.123.123    status^   |
└──────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;/td&gt;&lt;td&gt;
&lt;img src="/assets/screenshots/iWebTools_check.png" alt="Check Websites" /&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;h3&gt;Check Websites (in a website) tab&lt;/h3&gt;
&lt;p&gt;When the user clicks on a website, the user should see more information about the website.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Design&lt;/th&gt;&lt;th&gt;Result&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;
&lt;tr&gt;&lt;td&gt;&lt;pre&gt;&lt;code class="language-plaintext"&gt;
    https://wesleydegroot.nl
┌──────────────────────────────┐
| Favicon - Site title         |
| Technology: Build with tech  |
└──────────────────────────────┘
&lt;p&gt;┌──────────────────────────────┐&lt;br&gt;
| Validate HTML &amp;amp; CSS        ❯ |&lt;br&gt;
| Debug Tools                ❯ |&lt;br&gt;
| DOM Tree                   ❯ |&lt;br&gt;
| Check HTTP Headers         ❯ |&lt;br&gt;
| DNS Entries                ❯ |&lt;br&gt;
| Whois                      ❯ |&lt;br&gt;
| HTML Tag Usage             ❯ |&lt;br&gt;
| Display HTML               ❯ |&lt;br&gt;
└──────────────────────────────┘&lt;br&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;/td&gt;&lt;td&gt;
&lt;img src="/assets/screenshots/iWebTools_check_in.png" alt="Check Websites" /&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;h3&gt;Check Websites: Whois&lt;/h3&gt;
&lt;p&gt;The Whois tab should show the whois information of the domain.&lt;br&gt;
The user should be able to see the domain status, the DNSsec status, the creation date and the update date.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Design&lt;/th&gt;&lt;th&gt;Result&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;
&lt;tr&gt;&lt;td&gt;&lt;pre&gt;&lt;code class="language-plaintext"&gt;
┌──────────────────────────────┐
| Domain:     wesleydegroot.nl |
| Domain Status:        Active |
| DNSSec Status:           yes |
| Creation Date:    2015-10-16 |
| Update Date:      2024-06-16 |
| RAW Whois                  ❯ |
└──────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;/td&gt;&lt;td&gt;
&lt;img src="/assets/screenshots/iWebTools_whois.png" alt="Whois" /&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;h3&gt;Tools tab&lt;/h3&gt;
&lt;p&gt;The tools tab should have a list of tools, the user should be able to select a tool.&lt;br&gt;
The tools should be categorized in groups.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Design&lt;/th&gt;&lt;th&gt;Result&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;
&lt;tr&gt;&lt;td&gt;&lt;pre&gt;&lt;code class="language-plaintext"&gt;
             Tools
HTTP
┌──────────────────────────────┐
| HTTP Status codes          ❯ |
| HTTP Request               ❯ |
└──────────────────────────────┘
&lt;br&gt;
Converters
┌──────────────────────────────┐
| Crontab converter          ❯ |
| Color converter            ❯ |
└──────────────────────────────┘
&lt;br&gt;
Formatters
┌──────────────────────────────┐
| JSON Formatter             ❯ |
└──────────────────────────────┘
&lt;br&gt;
Encoders/Decoders
┌──────────────────────────────┐
| Base64                     ❯ |
| JWT Decoder                ❯ |
| URL                        ❯ |
└──────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;/td&gt;&lt;td&gt;
&lt;img src="/assets/screenshots/iWebTools_tools.png" alt="Tools" /&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;h3&gt;About tab&lt;/h3&gt;
&lt;p&gt;The about/settings tab is a modern design, which looks like a settings page.&lt;br&gt;
The user should be able to rate the app, give feedback, read the privacy policy and see the changelog.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Design&lt;/th&gt;&lt;th&gt;Result&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;
&lt;tr&gt;&lt;td&gt;&lt;pre&gt;&lt;code class="language-plaintext"&gt;
           About
┌──────────────────────────────┐
|            LOGO              |
|            LOGO              |
|            LOGO              |
|          iWebTools           |
|  Created by Wesley de Groot  |
└──────────────────────────────┘
&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
⊡ Application
┌──────────────────────────────┐
| Changelog                  ❯ |
| Privacy policy               |
| Rate the app                 |
| Feedback                     |
└──────────────────────────────┘
&lt;p&gt;⊡ About the developer&lt;br&gt;
┌──────────────────────────────┐&lt;br&gt;
| X/Twitter                    |&lt;br&gt;
| Bluesky                      |&lt;br&gt;
| Mastodon                     |&lt;br&gt;
| More apps from the developer |&lt;br&gt;
└──────────────────────────────┘&lt;br&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;/td&gt;&lt;td&gt;
&lt;img src="/assets/screenshots/iWebTools_about.png" alt="About" /&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;h2&gt;Tool: JWT Decoder&lt;/h2&gt;
&lt;p&gt;The JWT Decoder is a tool to convert a crontab to a human readable format.&lt;br&gt;
This was a harder tool to build, because i wanted to have (syntax) highlighting in the input and output.&lt;/p&gt;
&lt;p&gt;To do this, I used a &lt;code&gt;ZStack&lt;/code&gt; to overlay the &lt;code&gt;TextField&lt;/code&gt; with the highlighted text.&lt;br&gt;
The &lt;code&gt;TextField&lt;/code&gt; is above the highlighted text, so the user can still edit the text.&lt;br&gt;
In the code below I'll explain how I did it.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct JWTView: View {
    // Save the JWT Token
    @State private var JWTToken = ""

    var body: some View {
        List {
            Section("Input") {
                // ZStack is used to overlay the textfield with the highlighted text
                ZStack {
                    // Hightlighted text
                    Highlight(text: $JWTToken)

                    // The textfield is above the highlighted text
                    // so the user can still edit the text
                    TextField(
                        "JWT Token",
                        text: $JWTToken,
                        axis: .vertical
                    )
                    // The line limit is set to 5, so the textfield
                    // will not grow, and the space will be reserved
                    .lineLimit(5, reservesSpace: true)
                    // Since we don't want this text field to be shown,
                    // we set the background and foreground color to clear
                    .background(Color.clear)
                    .foregroundStyle(.clear)
                }
            }
        }
    }


    @ViewBuilder
    func Highlight(text: Binding&amp;lt;String&amp;gt;) -&amp;gt; some View {
        // Split the JWT Token into 3 parts
        let tokenData = JWTToken.components(separatedBy: ".")

        // Check if the token is not empty and has 3 parts
        if !JWTToken.isEmpty, tokenData.count == 3 {
            // Create a group to combine the 3 parts
            Group {
                if #available(iOS 17.0, *) {
                    // Part 1 is red
                    Text(tokenData[0])
                        .foregroundStyle(.red)
                    + Text(".") +
                    // Part 2 is purple
                    Text(tokenData[1])
                        .foregroundStyle(.purple)
                    + Text(".") +
                    // Part 3 (Signature) is blue
                    Text(tokenData[2])
                        .foregroundStyle(.blue)
                } else {
                    // No iOS 17.0, so we use the fallback
                    FallbackView(text: text)
                }
            }
            // The line limit is set to 5, so the line
            // will not grow, and the space will be reserved
            .lineLimit(5, reservesSpace: true)
            // The text is aligned to the leading side (left) in most cases
            .multilineTextAlignment(.leading)
            // Make the text fill the width of the screen
            .frame(maxWidth: .infinity, alignment: .leading)
        } else {
            // The token is empty or has less/more than 3 parts
            FallbackView(text: text)
        }
    }

    @ViewBuilder
    func FallbackView(text: Binding&amp;lt;String&amp;gt;) -&amp;gt; some View {
        Text(text.wrappedValue)
            // The line limit is set to 5, so the line
            // will not grow, and the space will be reserved
            .lineLimit(5, reservesSpace: true)
            // The text is aligned to the leading side (left) in most cases
            .multilineTextAlignment(.leading)
            // Make the text fill the width of the screen
            .frame(maxWidth: .infinity, alignment: .leading)
    }&lt;/code&gt;&lt;/pre&gt;
&lt;details&gt;
&lt;summary&gt;Click here to see the end result (screenshot)&lt;/summary&gt;
&lt;img src="/assets/screenshots/iWebTools_jwt.png" alt="JWT" /&gt;
&lt;/details&gt;
&lt;h2&gt;Tool: crontab converter&lt;/h2&gt;
&lt;p&gt;The crontab converter is a tool to convert a crontab to a human readable format.&lt;br&gt;
I made this tool also available as a &lt;a href="https://github.com/0xWDG/SwiftCronParser"&gt;open source project on GitHub&lt;/a&gt;, so other developers can use it in their projects.&lt;br&gt;
The code below is a fully working example of the crontab converter and how to use the &lt;code&gt;SwiftCronParser&lt;/code&gt; library, this is used in iWebtools.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI
import SwiftCronParser

struct CronConverterView: View {
    /// The input text
    @State
    private var cronText = ""

    /// The parsed cron time/converted data
    @State
    private var cronTime: SwiftCronParser.CronTime?

    var body: some View {
        List {
            VStack(alignment: .leading) {
                ZStack {
                    Highlight(text: $cronText)
                        .padding(2)
                        .monospaced()

                    TextField(
                        "Cron text",
                        text: $cronText
                    )
                    .padding(2)
                    .monospaced()
                    .lineLimit(1, reservesSpace: true)
                    .background(Color.clear)
                    .foregroundStyle(.clear)
                }
                .border(.black, width: 1)

                Text("Format: Minute Hour Day Month Weekday")
                    .monospaced()
                    .lineLimit(1)
                    .font(.footnote)
            }

            Section("Translated") {
                // Check if the cron time is not nil (we habe parsed the value)
                if let cronTime,
                    // Check if there is no error
                    cronTime.error == nil,
                    // Check if the cron time is valid
                    cronTime.isValid {
                    LabeledContent(
                        "Hour",
                        value: "\(cronTime.hour.map { String($0) }.joined(separator: ","))"
                    )
                    .foregroundStyle(.red)
                    LabeledContent(
                        "Minute",
                        value: "\(cronTime.minute.map { String($0) }.joined(separator: ","))"
                    )
                    .foregroundStyle(.purple)
                    LabeledContent(
                        "Day",
                        value: "\(cronTime.day.map { String($0) }.joined(separator: ","))"
                    )
                    .foregroundStyle(.blue)
                    LabeledContent(
                        "Month",
                        value: "\(cronTime.months.joined(separator: ", "))"
                    )
                    .foregroundStyle(.green)
                    LabeledContent(
                        "Day of week",
                        value: "\(cronTime.daysOfWeek.joined(separator: ", "))"
                    )
                    .foregroundStyle(.orange)
                } else {
                    if !cronText.isEmpty, let error = cronTime?.error {
                        Text(error)
                    }
                }
            }
        }
        .onChange(of: cronText) { val in
            // When the cron text changes, we parse the text
            cronTime = SwiftCronParser(input: cronText).parse()
        }
        .task {
#if DEBUG
            // If we are in debug mode, we set the cron text to a default value
            // so we can test the converter without typing
            cronText = "5 4 * * *"
            cronTime = SwiftCronParser(input: cronText).parse()
#endif
        }
        .navigationTitle("Crontab Converter")
        .navigationBarTitleDisplayMode(.inline)
    }

    // Here we do the same as in the JWT Decoder
    // We highlight if the input can be separated by a space into 5 parts
    @ViewBuilder
    func Highlight(text: Binding&amp;lt;String&amp;gt;) -&amp;gt; some View {
        let tokenData = text.wrappedValue.components(separatedBy: " ")
        if !text.wrappedValue.isEmpty,
           tokenData.count == 5 {
            Group {
                if #available(iOS 17.0, *) {
                    Text(tokenData[0])
                        .foregroundStyle(.red)
                    + Text(" ") +
                    Text(tokenData[1])
                        .foregroundStyle(.purple)
                    + Text(" ") +
                    Text(tokenData[2])
                        .foregroundStyle(.blue)
                    + Text(" ") +
                    Text(tokenData[3])
                        .foregroundStyle(.green)
                    + Text(" ") +
                    Text(tokenData[4])
                        .foregroundStyle(.orange)
                } else {
                    FallbackView(text: text)
                }
            }
            .lineLimit(1, reservesSpace: true)
            .multilineTextAlignment(.leading)
            .frame(maxWidth: .infinity, alignment: .leading)
        } else {
            FallbackView(text: text)
        }
    }

    @ViewBuilder
    func FallbackView(text: Binding&amp;lt;String&amp;gt;) -&amp;gt; some View {
        Text(text.wrappedValue)
            .lineLimit(1, reservesSpace: true)
            .multilineTextAlignment(.leading)
            .frame(maxWidth: .infinity, alignment: .leading)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;details&gt;
&lt;summary&gt;Click here to see the end result (screenshot)&lt;/summary&gt;
&lt;img src="/assets/screenshots/iWebTools_cron.png" alt="Cron" /&gt;
&lt;/details&gt;
&lt;h3&gt;Download iWebTools&lt;/h3&gt;
&lt;p&gt;You can download iWebTools from the &lt;a class='button' href='https://apps.apple.com/us/app/iwebtools-2/id642432354' target='_blank' class='button'&gt;&lt;i class="fa-brands fa-app-store"&gt;&lt;/i&gt; AppStore&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Ideas&lt;/h3&gt;
&lt;p&gt;If you have any ideas for iWebTools, please let me know in the comments below!&lt;/p&gt;</description>
  </item>
  <item>
    <title>NumberFormatter</title>    <link>https://wesleydegroot.nl/blog/numberformatter</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/numberformatter</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:20 +0200</pubDate>
    <category>NumberFormatter</category>
    <category>Swift</category>
    <description>&lt;p&gt;NumberFormatter is a class that provides a flexible and easy way to convert numbers into strings and vice versa.&lt;br&gt;
It is a part of the Foundation framework and is available on all Apple platforms.&lt;br&gt;
In this post, we will explore the various ways in which we can use NumberFormatter to format numbers in Swift.&lt;/p&gt;
&lt;h2&gt;What Styles does &lt;code&gt;NumberFormatter&lt;/code&gt; have?&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Style&lt;/th&gt;
&lt;th&gt;en_US Locale&lt;/th&gt;
&lt;th&gt;nl_NL Locale&lt;/th&gt;
&lt;th&gt;zh_CN Locale&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;NumberFormatter.Style.none&lt;/td&gt;
&lt;td&gt;1235&lt;/td&gt;
&lt;td&gt;1235&lt;/td&gt;
&lt;td&gt;1235&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NumberFormatter.Style.decimal&lt;/td&gt;
&lt;td&gt;1,234.568&lt;/td&gt;
&lt;td&gt;1.234,568&lt;/td&gt;
&lt;td&gt;1,234.568&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NumberFormatter.Style.percent&lt;/td&gt;
&lt;td&gt;12%&lt;/td&gt;
&lt;td&gt;12 %&lt;/td&gt;
&lt;td&gt;12%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NumberFormatter.Style.scientific&lt;/td&gt;
&lt;td&gt;1.2345678E3&lt;/td&gt;
&lt;td&gt;1,2345678E3&lt;/td&gt;
&lt;td&gt;1.2345678E3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NumberFormatter.Style.spellOut&lt;/td&gt;
&lt;td&gt;one hundred twenty-three&lt;/td&gt;
&lt;td&gt;honderddrieëntwintig&lt;/td&gt;
&lt;td&gt;一百二十三&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NumberFormatter.Style.ordinal&lt;/td&gt;
&lt;td&gt;3rd&lt;/td&gt;
&lt;td&gt;3e&lt;/td&gt;
&lt;td&gt;第3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NumberFormatter.Style.currency&lt;/td&gt;
&lt;td&gt;$1,234.57&lt;/td&gt;
&lt;td&gt;€ 1.234,57&lt;/td&gt;
&lt;td&gt;￥1,234.57&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NumberFormatter.Style.currencyAccounting&lt;/td&gt;
&lt;td&gt;($1,234.57)&lt;/td&gt;
&lt;td&gt;(€ 1.234,57)&lt;/td&gt;
&lt;td&gt;(￥1,234.57)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NumberFormatter.Style.currencyISOCode&lt;/td&gt;
&lt;td&gt;USD1,234.57&lt;/td&gt;
&lt;td&gt;EUR 1.234,57&lt;/td&gt;
&lt;td&gt;CNY1,234.57&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NumberFormatter.Style.currencyPlural&lt;/td&gt;
&lt;td&gt;1,234.57 US dollars&lt;/td&gt;
&lt;td&gt;1.234,57 euro&lt;/td&gt;
&lt;td&gt;1,234.57 人民币&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Force a specific locale&lt;/h2&gt;
&lt;p&gt;By default, &lt;code&gt;NumberFormatter&lt;/code&gt; uses the current locale of the device.&lt;br&gt;
You can force a specific locale by setting the &lt;code&gt;locale&lt;/code&gt; property of the &lt;code&gt;NumberFormatter&lt;/code&gt; class.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;let numberFormatter = NumberFormatter()
numberFormatter.locale = Locale(identifier: "nl_NL")
print(numberFormatter.string(from: 1000)!) // prints "1000" in the Dutch locale&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Use Cases&lt;/h2&gt;
&lt;h3&gt;Formatting Numbers&lt;/h3&gt;
&lt;p&gt;One of the most common use cases for &lt;code&gt;NumberFormatter&lt;/code&gt; is to format numbers.&lt;br&gt;
This can be done using the &lt;code&gt;numberStyle&lt;/code&gt; property of the &lt;code&gt;NumberFormatter&lt;/code&gt; class.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
print(numberFormatter.string(from: 1000)!) // prints "1,000" in the US locale, "1000" in the Dutch locale&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Number to Words (Spell Out)&lt;/h3&gt;
&lt;p&gt;Another case for &lt;code&gt;NumberFormatter&lt;/code&gt; is to convert a number into a string.&lt;br&gt;
This can be done using the &lt;code&gt;string(from: Number)&lt;/code&gt; function.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .spellOut
print(numberFormatter.string(from: 1990)!) // prints "one thousand nine hundred ninety"&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Currency&lt;/h3&gt;
&lt;p&gt;Another use case for &lt;code&gt;NumberFormatter&lt;/code&gt; is to format numbers as currency.  .&lt;br&gt;
This can be done using the &lt;code&gt;currencySymbol&lt;/code&gt; and &lt;code&gt;currencyCode&lt;/code&gt; properties of the &lt;code&gt;NumberFormatter&lt;/code&gt; class.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .currency
numberFormatter.currencyCode = "EUR"
print(numberFormatter.string(from: 10)!) // prints "€10.00"&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;NumberFormatter is a powerful class that provides a flexible and easy way to convert numbers into strings and vice versa.&lt;br&gt;
It supports a wide range of styles and locales, making it suitable for a variety of use cases.&lt;br&gt;
Whether you need to format numbers, convert numbers to words, or display numbers as currency, NumberFormatter has you covered.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/foundation/numberformatter"&gt;https://developer.apple.com/documentation/foundation/numberformatter&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Tips and ideas to start your own blog</title>    <link>https://wesleydegroot.nl/blog/start-your-own-blog</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/start-your-own-blog</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:21 +0200</pubDate>
    <category>Swift</category>
    <category>Blog</category>
    <description>&lt;p&gt;When I started &lt;a href="/blog/new-website"&gt;this website&lt;/a&gt; on 03 February 2024 I was unsure if I would be able to keep it up. I had tried to start a blog some times before but I always struggled to keep it up. This time I take another approach and I am happy to say that I have been able to keep it up for over a year now. In this blog post I want to share some tips and ideas on how to start your own blog about Swift.&lt;/p&gt;
&lt;h2&gt;Benefits of having a blog&lt;/h2&gt;
&lt;p&gt;For me the benefits of writing blogs are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Learning: I learn a lot by writing blogs. I have to research the topic, understand it and then write about it. This helps me to understand the topic better.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Sharing: I can share my knowledge with others. This is a great way to give back to the community.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;(Private) problem and solution database: I can use my blog to search for problems you have solved in the past.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Networking: I can connect with other developers who are interested in the same topics.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Cons of having a blog&lt;/h2&gt;
&lt;p&gt;The only con I can think of is that it takes time. Writing a blog post takes time and effort. But I think the benefits outweigh the cons.&lt;/p&gt;
&lt;h2&gt;The system I use for blogging.&lt;/h2&gt;
&lt;p&gt;This will be discussed in a future blog post &lt;a href="/blog/What-powers-this-website"&gt;What powers this website&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Finding topics&lt;/h2&gt;
&lt;p&gt;When I started this blog I subscribed to a lot of other blogs to get email updates from them. If there is a topic that I find interesting I open a issue on Github, I add the category and a indication of the difficulty of the topic. I also add a link to the blog post that inspired me to write about it.&lt;br&gt;
Another way for finding topics is, problems I have solved in the past, or new things I have learned.&lt;/p&gt;
&lt;h2&gt;Managing topics &amp;amp; time&lt;/h2&gt;
&lt;p&gt;I often dont feel like I want to write a blog post, but I have a schedule to keep up with (every Tuesday).&lt;br&gt;
What I do is, I write blogs in advance, this post is made on 05-FEB-2025. I usually have for a couple of weeks in advance a blog post ready to be published.&lt;br&gt;
I also have placeholders for some future posts, e.g. &lt;a href="/blog/Hacktoberfest-2025"&gt;Hacktoberfest 2025&lt;/a&gt; and some posts for Hacktoberfest about some Swift Packages I've created (&lt;a href="/blog/Swift-Package-ImagePicker"&gt;ImagePicker&lt;/a&gt;, &lt;a href="/blog/Swift-Package-FilePicker"&gt;FilePicker&lt;/a&gt;, &lt;a href="/blog/Swift-Package-iCloudStorage"&gt;iCloudStorage&lt;/a&gt;, &lt;a href="/blog/Swift-Package-SecureStorage"&gt;SecureStorage&lt;/a&gt;, &lt;a href="/blog/Swift-Package-XCUITestHelper"&gt;XCUITestHelper&lt;/a&gt;, &lt;a href="/blog/Swift-Package-PreventScreenshot"&gt;PreventScreenshot&lt;/a&gt;, &lt;a href="/blog/Swift-Package-NetworkMonitor"&gt;NetworkMonitor&lt;/a&gt;, &lt;a href="/blog/Swift-Package-SwiftCronParser"&gt;SwiftCronParser&lt;/a&gt;, &lt;a href="/blog/Swift-Package-SwiftExtras"&gt;SwiftExtras&lt;/a&gt;).&lt;/p&gt;
&lt;h2&gt;Creating a blog post&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;My target audience does not like reading (i think) and I don’t like writing, so keep it short and focused&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I do think I'm still a beginner in blogging. I have been able to keep it up for over a year now.&lt;br&gt;
I have tried various formats and writing styles, I have a template that I use for every blog post.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
&amp;lt;post‎ title="SUBJECT" tags="TAGS"&amp;gt;&lt;/code&gt;&lt;/pre&gt;</description>
  </item>
  <item>
    <title>What powers this website</title>    <link>https://wesleydegroot.nl/blog/what-powers-this-website</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/what-powers-this-website</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:22 +0200</pubDate>
    <category>Blog</category>
    <category>PHP</category>
    <description>&lt;p&gt;When I started &lt;a href="/blog/new-website"&gt;this website&lt;/a&gt; on 03 February 2024, it was a small project, with one php file for rendering markdown files.&lt;/p&gt;
&lt;p&gt;This website is made in Visual Studio Code, with plain HTML, CSS, and a little bit of JavaScript (for opening the external URL's in a new tab, and add some utm tags to it, and to support comments on the blogposts).&lt;/p&gt;
&lt;p&gt;The "backend" is written in PHP.&lt;/p&gt;
&lt;h2&gt;Page: Home&lt;/h2&gt;
&lt;p&gt;The home page is the page where the visitor (you) lands on, it shows a small introduction, my latest blog post, my latest mastodon post, a random app, and my latest github action.&lt;/p&gt;
&lt;h2&gt;Page: Blog&lt;/h2&gt;
&lt;p&gt;The blog page is the part which takes care of the blogposts, it reads the blogposts from a folder and parses them, the post format is &lt;code&gt;yyyymmdd_post-title.md&lt;/code&gt; if a post must remain hidden it contains a &lt;code&gt;~&lt;/code&gt; in the filename.&lt;/p&gt;
&lt;h2&gt;Page: Apps&lt;/h2&gt;
&lt;p&gt;The apps page is a page where I show my apps, it reads the apps from a json file and display them, if there is a bundle identifier defined, fetch data from the AppStore (such as the name, price, description, last update, versionnumber and screenshots).&lt;/p&gt;
&lt;p&gt;example of an app:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-json"&gt;"Calendo": {
    "name": "Calendo",
    "bundle_id": "nl.wesleydegroot.Calendo",
    "description": "Calendo: Your Simple and Smart Calendar\n\nStay organized and on top of your schedule with Calendo,\nthe ultimate calendar app designed for simplicity and efficiency.\nWhether you're managing personal appointments, work meetings,\nor family events, Calendo makes it easy to keep track of everything in one place.\n\nKey Features:\n  - Easy Event Management: Quickly add, edit, and delete events with a few taps.\n  - Scrollable month views: No more hassle of switching screens.\n  - Offline Access: Access your calendar even without an internet connection, because it uses your system calendar!\n\nWhy Choose Calendo?\n  - User-Friendly Interface: Designed with simplicity in mind, Calendo is easy to navigate and use.\n  - Reliable and Secure: We don't gather your calendar data.\n",
    "tags": [
        "iOS"
    ],
    "category": "Productivity,Utility",
    "url": {
        "AppStore": "https:\/\/apps.apple.com\/us\/app\/calendo\/id6651862759",
        "Issue tracker": "https:\/\/github.com\/0xWDG\/calendo-issues"
    },
    "screenshots": [
        "\/assets/screenshots\/Calendo_welcome.png",
        "\/assets/screenshots\/Calendo_calendar.png",
        "\/assets/screenshots\/Calendo_details.png"
    ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Page: Projects&lt;/h2&gt;
&lt;p&gt;The projects page is a page where I show my projects, it reads the projects from a json file and fetch information from the GitHub API (amount of stars, forks, last update, and description).&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-json"&gt;[
    "AuroraEditor/AuroraEditor",
    "0xWDG/Admob-SwiftUI",
    "0xWDG/Apple-Music-to-Discord",
    "0xWDG/build-documentation",
    "0xWDG/CachedAsyncImage",
    "0xWDG/Colors",
    "0xWDG/DynamicUI",
    "0xWDG/FilePicker",
    "0xWDG/GameControllerKit",
    "0xWDG/iCloudStorage",
    "0xWDG/ImagePicker",
    "0xWDG/Inspect",
    "0xWDG/NetworkMonitor",
    "0xWDG/OnboardingKit",
    "0xWDG/OSLogViewer",
    "0xWDG/PHPFramework",
    "0xWDG/PreventScreenshot",
    "0xWDG/SecureStorage",
    "0xWDG/SimpleNetworking",
    "0xWDG/SwiftCronParser",
    "0xWDG/SwiftExtras",
    "0xWDG/tvOS.js",
    "0xWDG/XCUITestHelper"
]&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Item: Sitemap&lt;/h2&gt;
&lt;p&gt;This is a php file that generates a sitemap.xml file, containing all the pages, blogposts, apps, and projects.&lt;/p&gt;
&lt;h2&gt;Item: RSS&lt;/h2&gt;
&lt;p&gt;This is a php file that generates a rss.xml file, containing all blogposts.&lt;/p&gt;
&lt;h2&gt;Item: GitHub API.&lt;/h2&gt;
&lt;p&gt;This is used for my &lt;a href="https://github.com/0xWDG"&gt;GitHub profile&lt;/a&gt; profile, it gives a random blogpost, latest blogpost, a random app, and a random project for the README.md file.&lt;/p&gt;
&lt;h2&gt;Item: Social media API.&lt;/h2&gt;
&lt;p&gt;This script post to Bluesky, Mastodon and Threads, it uses an &lt;a href="https://apyhub.com/"&gt;AI&lt;/a&gt; to summarize the blogpost.&lt;/p&gt;
&lt;h2&gt;Extra: Mastodon instance&lt;/h2&gt;
&lt;p&gt;This website is also running a (not yet working) &lt;a href="https://mastodon.social/@0xWDG@wesleydegroot.nl"&gt;Mastodon instance&lt;/a&gt;, I want to use this to post my blogposts to it.&lt;/p&gt;
&lt;h2&gt;Fun pages&lt;/h2&gt;
&lt;p&gt;I've made some funny pages for some HTTP status codes, such as&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/401"&gt;401 (Unauthorized)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/402"&gt;402 (Payment Required)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/403"&gt;403 (Forbidden)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/404"&gt;404 (Page not found)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/451"&gt;451 (Unavailable For Legal Reasons)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Building xcstrings-translator</title>    <link>https://wesleydegroot.nl/blog/building-xcstrings-translator</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/building-xcstrings-translator</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:22 +0200</pubDate>
    <category>Swift</category>
    <category>xcstrings</category>
    <description>&lt;p&gt;xcstrings-translator is a tool to (easily) translate &lt;code&gt;.xcstrings&lt;/code&gt; files, It is a GUI tool, written in Swift and SwiftUI to translate &lt;code&gt;.xcstrings&lt;/code&gt; files to different languages using Apple's &lt;a href="/blog/Translation-framework-in-Swift"&gt;&lt;code&gt;Translation&lt;/code&gt; Framework&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Idea&lt;/h2&gt;
&lt;p&gt;The idea behind this tool is to provide a simple and easy way to translate &lt;code&gt;.xcstrings&lt;/code&gt; files. The tool should be able to read the &lt;code&gt;.xcstrings&lt;/code&gt; file, my original idea was to make this a commandline application, but the Translation framework needs a SwiftUI view to work, so I decided to make this a GUI tool, then it needs to show the strings in a table view, and allow the user to translate the strings. The tool should also be able to save the translated strings back to the &lt;code&gt;.xcstrings&lt;/code&gt; file.&lt;/p&gt;
&lt;h2&gt;Implementation&lt;/h2&gt;
&lt;p&gt;The tool is implemented using Swift and SwiftUI. The tool uses the &lt;code&gt;Translation&lt;/code&gt; framework to translate the strings. The tool reads the &lt;code&gt;.xcstrings&lt;/code&gt; file, extracts the strings, and shows them in a table view. The user can then translate the strings and save the translated strings back to the &lt;code&gt;.xcstrings&lt;/code&gt; file.&lt;/p&gt;
&lt;h2&gt;Layout&lt;/h2&gt;
&lt;p&gt;The tool has a simple layout. The main window has a table view to show the strings. The table view has two columns, one for the original string and one for the translated string.&lt;/p&gt;
&lt;p&gt;&lt;img src="/assets/screenshots/xcstrings-translator.png" alt="xcstrings-translator" loading="lazy"&gt;&lt;/p&gt;
&lt;h2&gt;The build process&lt;/h2&gt;
&lt;h3&gt;Building the layout&lt;/h3&gt;
&lt;p&gt;The layout is built using SwiftUI.&lt;br&gt;
The main window has a "table view" to show the strings, and some elements to interact. &lt;/p&gt;
&lt;p&gt;The view has two columns, one for the original string and one for the translated string.&lt;br&gt;
This is achived using the &lt;code&gt;LabeledContent&lt;/code&gt; view.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;List {
    ForEach(languageParser.stringsToTranslate, id: \.self) { string in
        HStack {
            LabeledContent(
                string,
                value: translatedStrings[string] ?? ""
            )
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Opening and saving the &lt;code&gt;.xcstrings&lt;/code&gt; file&lt;/h3&gt;
&lt;p&gt;To allow the user to open and save the &lt;code&gt;.xcstrings&lt;/code&gt; file, the tool uses my &lt;a href="/blog/Swift-Package-FilePicker"&gt;&lt;code&gt;FilePicker&lt;/code&gt;&lt;/a&gt; package.&lt;br&gt;
The &lt;code&gt;FilePicker&lt;/code&gt; package provides a simple way to open and save files in SwiftUI.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;.cxstrings&lt;/code&gt; file format&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-json"&gt;{
  "sourceLanguage" : "en",
  "strings" : {
    "More apps from the developer" : {
      "extractionState" : "translated",
      "localizations" : {
        "de" : {
          "stringUnit" : {
            "state" : "translated",
            "value" : "Weitere Apps vom Entwickler"
          }
        },
        "fr" : {
          "stringUnit" : {
            "state" : "translated",
            "value" : "Plus d'applications du développeur"
          }
        },
        "nl" : {
          "stringUnit" : {
            "state" : "translated",
            "value" : "Meer apps van de ontwikkelaar"
          }
        }
      }
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Reading the &lt;code&gt;.xcstrings&lt;/code&gt; file&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;.cxstrings&lt;/code&gt; file is a easy file to read, the only "tricky" part is that the keys in this JSON file are the strings to translate, so we can't use a &lt;code&gt;Codable&lt;/code&gt; struct to decode the file.&lt;/p&gt;
&lt;p&gt;To read the &lt;code&gt;.xcstrings&lt;/code&gt; file, we do this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;class LanguageParser: ObservableObject {
    /// Create a language dictionary to store the strings
    @Published var languageDictionary: [String: Any] = [:]

    /// Create an array to store the strings to translate
    @Published var stringsToTranslate: [String] = []

    /// Create an array to store if the string should be translated 
    /// (there is a better way to do this)
    @Published var shouldTranslate: [Bool] = []

    func load(file url: URL) {
        do {
            /// Read the file
            if let data = try? Data(contentsOf: url),
               /// Try to decode the file to a [String: Any] dictionary.
               let dict = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
                /// Save the dictionary
                languageDictionary = dict

                /// Parse the dictionary
                parse()
            }
        } catch {
            // Error
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Parsing the &lt;code&gt;.xcstrings&lt;/code&gt; file&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;extension LanguageParser {
    func parse() {
        stringsToTranslate = []

        if let strings = languageDictionary["strings"] as? [String: Any] {
            for (key, value) in strings where !key.isEmpty {
                guard let value = value as? [String: Any] else { continue }
                stringsToTranslate.append(key)
                shouldTranslate.append(value["shouldTranslate"] as? Bool ?? true)
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Exposing the JSON data&lt;/h3&gt;
&lt;p&gt;To save the &lt;code&gt;.xcstrings&lt;/code&gt; file, we need to convert the language dictionary to JSON data and write the data to the file.&lt;br&gt;
The &lt;code&gt;LanguageParser&lt;/code&gt; class has a &lt;code&gt;data&lt;/code&gt; property that converts the language dictionary to JSON data, the &lt;code&gt;.filePicker&lt;/code&gt; modifier will be used to save the file later.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;extension LanguageParser {
    var data: Data {
        try! JSONSerialization.data(
            withJSONObject: languageDictionary,
            options: .prettyPrinted
        )
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Upating the translated strings&lt;/h3&gt;
&lt;p&gt;This is a bit complex to userstand, I made the mistake to not update the correct values to update the language dictionary.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;extension LanguageParser {
    func add(translation: String, forLanguage: String, original: String) {
        // We want to get the "strings" key from the dictionary
        if var strings = languageDictionary["strings"] as? [String: Any],
           // We want to get the original string from the dictionary
           var item = strings[original] as? [String: Any] {
            // strings -&amp;gt; "original string" -&amp;gt; localizations -&amp;gt; "language"

            // We want to check if there are already localizations
            if var localizations = item["localizations"] as? [String: Any] {
                // There is already a localization key, now we can update the value for this language
                localizations[forLanguage] = [
                    "stringUnit": [
                        "state": "translated", 
                        "value": translation
                    ]
                ]

                // Update the localizations key
                item["localizations"] = localizations

                // Save the item back to the strings dictionary
                strings[original] = item

                // Save the strings back to the language dictionary
                languageDictionary["strings"] = strings

                // Finish execution
                return
            } else {
                // There are no localizations yet, so we need to create a new one
                item["localizations"] = [
                    forLanguage: [
                        "stringUnit": [
                            "state": "translated",
                            "value": translation
                        ]
                    ]
                ]

                // Save the item back to the strings dictionary
                strings[original] = item

                // Save the strings back to the language dictionary
                languageDictionary["strings"] = strings

                // Finish execution
                return
            }
        }

        // Something went wrong
        print("Failed to get strings")
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Final application&lt;/h3&gt;
&lt;p&gt;To translate the strings we need to setup a TranslationSession, and in this example we are always translating from English to Dutch.&lt;br&gt;
Please note that this is a very simple example, for the best results you can look up the &lt;a href="https://github.com/0xWDG/xcstrings-translator/blob/main/xcstrings-translator/xcstrings-translator/ContentView.swift"&gt;actual code for this view on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct ContentView: View {
    /// This is the language parser we've built before.
    @ObservedObject var languageParser = LanguageParser()

    /// This is array which holds the translated strings
    @State var translatedStrings: [String: String] = [:]

    /// This is the source language (English)
    @State var sourceLanguage: Locale.Language?

    /// This is the destination language (Dutch)
    @State var destinationLanguage: Locale.Language?

    /// This is the translation session
    @State var translationSession: TranslationSession?

    /// Is the file picker open
    @State var filePickerOpen = false

    /// Is the file exporter open
    @State var exportFile = false

    /// Which files are selected
    @State var filePickerFiles: [URL] = []

    var body: some View {
        VStack {
            List {
                ForEach(languageParser.stringsToTranslate, id: \.self) { string in
                    HStack {
                        LabeledContent(
                            string,
                            value: translatedStrings[string] ?? ""
                        )
                    }
                }

                Button("Open", systemImage: "square.and.arrow.down") {
                    filePickerOpen.toggle()
                }
                // Support ⌘O to open
                .keyboardShortcut("o", modifiers: .command)

                Button("Save", systemImage: "square.and.arrow.up") {
                    exportFile.toggle()
                }
                // Support ⌘S to save
                .keyboardShortcut("s", modifiers: .command)

                Button("Translate", systemImage: "translate") {
                    translate() // Translate the strings
                }
                // Support ⌘T to translate
                .keyboardShortcut("t", modifiers: .command)
            }
        }
        .task {
            /// Set the destination language to Dutch
            destinationLanguage = supportedLanguages.first(where: { $0.languageCode == "nl" })

            /// Set the source language to English
            sourceLanguage = supportedLanguages.first(where: { $0.languageCode == "en" })
        }
        /// Open a file
        .filePicker(
            isPresented: $filePickerOpen,
            files: $filePickerFiles,
            types: [.init(filenameExtension: "xcstrings")!]
        )
        /// Save a file
        .filePicker(
            isPresented: $exportFile,
            fileName: "Localizable.strings",
            data: languageParser.data, // Exposed JSON data
            types: [.init(filenameExtension: "xcstrings")!]
        )
        /// File is opened
        .onChange(of: $filePickerFiles.wrappedValue) {
            if let val = $filePickerFiles.wrappedValue.first {
                // Reset the translated strings
                translatedStrings = [:]

                // Load the file
                languageParser.load(file: val)
            }
        }
        // Setup the translation session
        .translationTask(
            source: sourceLanguage,
            target: destinationLanguage
        ) { session in
            // Save our translation session
            translationSession = session
        }
    }

    func translate() {
        Task {
            if let session = translationSession {
                do {
                    for string in languageParser.stringsToTranslate where !string.isEmpty {
                        // Perform translation
                        let response = try await session.translate(string)

                        // Save the translated string
                        translatedStrings[string] = response.targetText

                        // Add the translation to the language parser (to save it to the file)
                        languageParser.add(translation: response)
                    }
                } catch {
                    // code to handle error
                }
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This was a fun project to work on, I've created a new Swift package to open and save files in SwiftUI, and I've had fun to work on this project.&lt;/p&gt;
&lt;h2&gt;Special thanks&lt;/h2&gt;
&lt;p&gt;Special thanks goes to &lt;a href="https://mastodon.social/@zhenyi/113969196950076700"&gt;Zhenyi Tan&lt;/a&gt; to help me with fixing the issue I've made which prevented me from updating the language dictionary.&lt;/p&gt;
&lt;h2&gt;Dependencies used&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Translation-framework-in-Swift"&gt;Translation framework&lt;/a&gt;&lt;br&gt;
This is a framework provided by Apple to translate strings.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/Swift-Package-FilePicker"&gt;FilePicker&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/0xWDG/FilePicker"&gt;FilePicker&lt;/a&gt; is a Swift package to open and save files in SwiftUI.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/0xWDG/SwiftExtras"&gt;SwiftExtras&lt;/a&gt;&lt;br&gt;
SwiftExtras is a collection of Swift extensions and utilities, It is mainly used to create the settings view.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/apps/xcstrings-translator"&gt;xcstrings-translator app page&lt;/a&gt;&lt;br&gt;
The app page for "xcstrings-translator" on my website.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/0xWDG/xcstrings-translator"&gt;xcstrings-translator GitHub repository&lt;/a&gt;&lt;br&gt;
The GitHub repository for "xcstrings-translator".&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Customizing macOS windows</title>    <link>https://wesleydegroot.nl/blog/customizing-macos-windows</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/customizing-macos-windows</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:23 +0200</pubDate>
    <category>SwiftUI</category>
    <category>macOS</category>
    <description>&lt;p&gt;MacOS windows are a fundamental part of the user interface, and customizing them can greatly enhance the user experience. In this post, we will explore how to customize macOS windows using SwiftUI.&lt;br&gt;
We will cover how to create a custom window style, add a toolbar. &lt;/p&gt;
&lt;h2&gt;Window backgrounds&lt;/h2&gt;
&lt;p&gt;To customize the background of a window, we can use the &lt;a href="https://developer.apple.com/documentation/swiftui/view/containerbackground%28_%3Afor%3A%29"&gt;&lt;code&gt;.containerBackground(_:for:)&lt;/code&gt;&lt;/a&gt; modifier. &lt;/p&gt;
&lt;h3&gt;Default window background&lt;/h3&gt;
&lt;p&gt;The default window background is a solid color. You can set the background color of a window using the &lt;code&gt;.background&lt;/code&gt; modifier.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/resources/windowstyle_default.png" alt="Default window background" loading="lazy"&gt;&lt;/p&gt;
&lt;h3&gt;thinMaterial window background&lt;/h3&gt;
&lt;p&gt;You can use the &lt;a href="https://developer.apple.com/documentation/swiftui/view/containerbackground%28_%3Afor%3A%29"&gt;&lt;code&gt;.containerBackground(_:for:)&lt;/code&gt;&lt;/a&gt; modifier to set a material background for the window. This will give the window a translucent effect, allowing the content behind it to show through.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .containerBackground(
                    .thinMaterial, for: .window
                )
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/resources/windowstyle_thinmaterial.png" alt="thinMaterial window background" loading="lazy"&gt;&lt;/p&gt;
&lt;h3&gt;Without toolbar&lt;/h3&gt;
&lt;p&gt;You can also hide the toolbar of the window by using the &lt;a href="https://developer.apple.com/documentation/swiftui/view/toolbarbackgroundvisibility(_:for:)"&gt;&lt;code&gt;.toolbarBackgroundVisibility(_:for:)&lt;/code&gt;&lt;/a&gt; modifier. This will remove the toolbar from the window, giving it a cleaner look.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .containerBackground(
                    .thinMaterial, for: .window
                )
                .toolbarBackgroundVisibility(
                    .hidden, for: .windowToolbar
                )
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/resources/windowstyle_withouttoolbar.png" alt="Without toolbar" loading="lazy"&gt;&lt;/p&gt;
&lt;h3&gt;Without title bar&lt;/h3&gt;
&lt;p&gt;You can also hide the title bar of the window by using the &lt;a href="https://developer.apple.com/documentation/swiftui/scene/windowstyle(_:)"&gt;&lt;code&gt;.windowStyle(_:)&lt;/code&gt;&lt;/a&gt; modifier. This will remove the title bar from the window, giving it a cleaner look.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .containerBackground(
                    .thinMaterial, for: .window
                )
        }
        .windowStyle(.hiddenTitleBar)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/resources/windowstyle_withouttitlebar.png" alt="Without title bar" loading="lazy"&gt;&lt;/p&gt;
&lt;h2&gt;Different toolbar styles&lt;/h2&gt;
&lt;p&gt;You can customize the toolbar style of a window by using the &lt;a href="https://developer.apple.com/documentation/swiftui/scene/windowtoolbarstyle(_:)"&gt;&lt;code&gt;.windowToolbarStyle(_:)&lt;/code&gt;&lt;/a&gt; modifier. This allows you to change the appearance of the toolbar to match your app's design.&lt;/p&gt;
&lt;h3&gt;Default toolbar style&lt;/h3&gt;
&lt;p&gt;The default toolbar style is combined with the window. This means that the toolbar will be displayed at the top of the window, and it will be combined with the window's title bar.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

@main
struct WindowApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .toolbar {
                    ToolbarItemGroup(placement: .automatic, content: {
                        Image(systemName: "star")
                        Text("You're a superstar")
                    })
                    ToolbarItemGroup(placement: .automatic, content: {
                        Menu(content: {
                            Button(action: {}, label: { Text("Sub item 1") })
                            Button(action: {}, label: { Text("Sub item 2") })
                        }, label: {
                            Text("Menu")
                        })
                    })
                }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/resources/toolbarstyles_default.png" alt="Different toolbar styles" loading="lazy"&gt;&lt;/p&gt;
&lt;h3&gt;Expanded toolbar style&lt;/h3&gt;
&lt;p&gt;The expanded toolbar style expands the toolbar to its full width, allowing for more space for the toolbar items. This is useful for apps that have a lot of toolbar items or need more space for their content.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

@main
struct WindowApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .toolbar {
                    ToolbarItemGroup(placement: .automatic, content: {
                        Image(systemName: "star")
                        Text("You're a superstar")
                    })
                    ToolbarItemGroup(placement: .automatic, content: {
                        Menu(content: {
                            Button(action: {}, label: { Text("Sub item 1") })
                            Button(action: {}, label: { Text("Sub item 2") })
                        }, label: {
                            Text("Menu")
                        })
                    })
                }
        }
        .windowToolbarStyle(.expanded)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/resources/toolbarstyles_expanded.png" alt="Expanded toolbar style" loading="lazy"&gt;&lt;/p&gt;
&lt;h3&gt;Unified toolbar style&lt;/h3&gt;
&lt;p&gt;The unified toolbar style combines the toolbar and the window title bar into a single element. This gives the window a more cohesive look and feel, making it easier for users to navigate.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

@main
struct WindowApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .toolbar {
                    ToolbarItemGroup(placement: .automatic, content: {
                        Image(systemName: "star")
                        Text("You're a superstar")
                    })
                    ToolbarItemGroup(placement: .automatic, content: {
                        Menu(content: {
                            Button(action: {}, label: { Text("Sub item 1") })
                            Button(action: {}, label: { Text("Sub item 2") })
                        }, label: {
                            Text("Menu")
                        })
                    })
                }
        }
        .windowToolbarStyle(.unified)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/resources/toolbarstyles_unified.png" alt="Unified toolbar style" loading="lazy"&gt;&lt;/p&gt;
&lt;h3&gt;Unified compact toolbar style&lt;/h3&gt;
&lt;p&gt;The unified compact toolbar style is similar to the unified toolbar style, but it is more compact. This is useful for apps that have not a lot of toolbar items.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

@main
struct WindowApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .toolbar {
                    ToolbarItemGroup(placement: .automatic, content: {
                        Image(systemName: "star")
                        Text("You're a superstar")
                    })
                    ToolbarItemGroup(placement: .automatic, content: {
                        Menu(content: {
                            Button(action: {}, label: { Text("Sub item 1") })
                            Button(action: {}, label: { Text("Sub item 2") })
                        }, label: {
                            Text("Menu")
                        })
                    })
                }
        }
        .windowToolbarStyle(.unifiedCompact)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/resources/windowstyle_unifiedcompact.png" alt="Unified compact toolbar style" loading="lazy"&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;MacOS windows are very customizable, and SwiftUI provides a powerful set of tools to help you create beautiful and functional windows. By using the techniques outlined in this post, you can create a unique and engaging user experience for your macOS applications.&lt;/p&gt;</description>
  </item>
  <item>
    <title>Creating macOS Menu Bar App in SwiftUI</title>    <link>https://wesleydegroot.nl/blog/creating-macos-menu-bar-app</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/creating-macos-menu-bar-app</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:23 +0200</pubDate>
    <category>SwiftUI</category>
    <category>macOS</category>
    <category>MenuBar</category>
    <description>&lt;p&gt;Creating a menu bar app may seems like a complicated task, but with SwiftUI, it can be done in a few simple steps. In this post, we will create a basic macOS menu bar app using SwiftUI.&lt;br&gt;
The app will display a menu bar icon and a simple menu with a few options. We will also add functionality to show a popover when the icon is clicked.&lt;/p&gt;
&lt;h2&gt;Setting up the Project&lt;/h2&gt;
&lt;p&gt;Create a new macOS project in Xcode and select "App" as the template. Make sure to select SwiftUI as the interface and Swift as the language.&lt;/p&gt;
&lt;p&gt;&lt;img src="/resources/menubar_project.png" alt="Create a new macOS project in Xcode" loading="lazy"&gt;&lt;/p&gt;
&lt;h2&gt;Creating the Menu Bar App&lt;/h2&gt;
&lt;p&gt;Your &lt;code&gt;MenuBarApp.swift&lt;/code&gt; file will look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

@main
struct MenuBarAppApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To support the menu bar, we need to make a slight change in this file.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;@main
struct MenuBarAppApp: App {
    var body: some Scene {
        MenuBarExtra {
            ContentView()
        } label: {
            Image(systemName: "star")
        }
        .menuBarExtraStyle(.window) // We use this to make the menu bar icon look like a window
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Creating the ContentView&lt;/h2&gt;
&lt;p&gt;Now, let's create a simple &lt;code&gt;ContentView&lt;/code&gt; that will be displayed when the menu bar icon is clicked. Create a new SwiftUI view file named &lt;code&gt;ContentView.swift&lt;/code&gt; and add the following code:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundStyle(.tint)
            Text("Hello, world!")
        }
        .padding()
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code creates a simple view with a text label and a button. When the button is clicked, it prints a message to the console.&lt;/p&gt;
&lt;h2&gt;Running the App&lt;/h2&gt;
&lt;p&gt;Now, you can run the app in Xcode.&lt;br&gt;
You should see a menu bar icon appear in the top right corner of your screen.&lt;br&gt;
&lt;img src="/resources/menubar_star.png" alt="Menu bar icon" loading="lazy"&gt;  &lt;/p&gt;
&lt;p&gt;When you click the icon, a popover with the text "Hello, World!" will appear.&lt;br&gt;
&lt;img src="/resources/menubar_contentview.png" alt="Popover" loading="lazy"&gt;&lt;/p&gt;
&lt;h2&gt;Hiding the Dock Icon&lt;/h2&gt;
&lt;p&gt;By default, the app will also show an icon in the dock.&lt;br&gt;
&lt;img src="/resources/menubar_icon.png" alt="Dock icon" loading="lazy"&gt;  &lt;/p&gt;
&lt;p&gt;To hide the dock icon, you need to modify the &lt;code&gt;Info.plist&lt;/code&gt; file.&lt;br&gt;
Navigate to your Project, click on the Target, and select the &lt;code&gt;Info&lt;/code&gt; tab, then add a new entry with the key &lt;code&gt;Application is agent (UIElement)&lt;/code&gt; and set its value to &lt;code&gt;YES&lt;/code&gt;.&lt;br&gt;
This will hide the app from the dock and make it a menu bar app.&lt;/p&gt;
&lt;p&gt;&lt;img src="/resources/menubar_applicationiselement.png" alt="Application is agent (UIElement)" loading="lazy"&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Creating a macOS menu bar app using SwiftUI is a easy process, it allows you to create a simple and functional app with minimal code.&lt;/p&gt;
&lt;p&gt;I hope this post helps you get started with creating your own macOS menu bar app using SwiftUI.&lt;/p&gt;
&lt;p&gt;Let me know in the comments which menu bar apps you've created or if you have any questions!&lt;/p&gt;
&lt;h2&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/menubarextra"&gt;https://developer.apple.com/documentation/swiftui/menubarextra&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Popovers in SwiftUI</title>    <link>https://wesleydegroot.nl/blog/popovers-in-swiftui</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/popovers-in-swiftui</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:24 +0200</pubDate>
    <category>SwiftUI</category>
    <description>&lt;p&gt;In SwiftUI, popovers are a great way to present additional information or options without navigating away from the current view. This post will explore how to create and customize popovers in SwiftUI.&lt;/p&gt;
&lt;h2&gt;Use Case for Popovers&lt;/h2&gt;
&lt;p&gt;Popovers are particularly useful when you want to display contextual information or options related to a specific element in your user interface. For example, you might use a popover to show additional details about an item in a list or to present settings options without cluttering the main view.&lt;/p&gt;
&lt;h2&gt;PresentationAdaptation struct&lt;/h2&gt;
&lt;p&gt;In SwiftUI, the &lt;a href="https://developer.apple.com/documentation/swiftui/presentationadaptation"&gt;&lt;code&gt;PresentationAdaptation&lt;/code&gt;&lt;/a&gt; struct is used to define how a view should adapt when presented in different environments, such as compact or regular size classes. This is particularly useful for popovers, as it allows them to behave appropriately across various device orientations and screen sizes.&lt;/p&gt;
&lt;p&gt;When you use the &lt;a href="https://developer.apple.com/documentation/swiftui/view/presentationcompactadaptation(_:)"&gt;&lt;code&gt;.presentationCompactAdaptation(_:)&lt;/code&gt;&lt;/a&gt; modifier, you can specify how the popover should adapt when the device is in a compact environment. For example, you might want the popover to appear as a full-screen modal in compact mode instead of a small popover.&lt;/p&gt;
&lt;p&gt;The options are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/presentationadaptation/automatic"&gt;&lt;code&gt;.automatic&lt;/code&gt;&lt;/a&gt;&lt;br&gt;
Use the default presentation adaptation.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/presentationadaptation/none"&gt;&lt;code&gt;.none&lt;/code&gt;&lt;/a&gt;&lt;br&gt;
Don’t adapt for the size class, if possible.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/presentationadaptation/fullscreencover"&gt;&lt;code&gt;.fullScreenCover&lt;/code&gt;&lt;/a&gt;&lt;br&gt;
Prefer a full-screen-cover appearance when adapting for size classes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/presentationadaptation/popover"&gt;&lt;code&gt;.popover&lt;/code&gt;&lt;/a&gt;&lt;br&gt;
Prefer a popover appearance when adapting for size classes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/presentationadaptation/sheet"&gt;&lt;code&gt;.sheet&lt;/code&gt;&lt;/a&gt;&lt;br&gt;
Prefer a sheet appearance when adapting for size classes.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Popover Modifiers&lt;/h2&gt;
&lt;p&gt;In this tutorial, we'll use 2 key modifiers to create and customize a popover in SwiftUI:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/popover%28ispresented%3Aattachmentanchor%3Aarrowedge%3Acontent%3A%29"&gt;&lt;code&gt;.popover(isPresented:attachmentAnchor:arrowEdge:content:)&lt;/code&gt;&lt;/a&gt;&lt;br&gt;
This modifier gives you some options to customize the popover's behavior. You can specify whether the popover is presented, where it should attach to the view, and which edge of the popover should point to the anchor.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/presentationcompactadaptation(_:)"&gt;&lt;code&gt;.presentationCompactAdaptation(_:)&lt;/code&gt;&lt;/a&gt;&lt;br&gt;
This modifier allows the popover to adapt to compact environments, such as when the device is in landscape mode or when the screen size is smaller. It ensures that the popover behaves appropriately across different device orientations and sizes.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Code&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct Popovers: View {
    @State
    private var popoverVisible: Bool = false

    var body: some View {
        Button("Toggle Popover") {
            popoverVisible.toggle()
        }
        .popover(
            isPresented: $popoverVisible, 
            arrowEdge: .top
        ) {
            popoverView
                .presentationCompactAdaptation(.popover)
                // This modifier allows the popover to adapt to compact environments
        }
    }

    var popoverView: some View {
        Text("You have pressed the button!")
            .padding()
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Screenshots&lt;/h2&gt;
&lt;h3&gt;iOS&lt;/h3&gt;
&lt;p&gt;&lt;img src="/resources/popover_ios.png" alt="iOS Popover Example" loading="lazy"&gt;&lt;/p&gt;
&lt;h3&gt;macOS&lt;/h3&gt;
&lt;p&gt;&lt;img src="/resources/popover_macos.png" alt="iOS Popover Example" loading="lazy"&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Popovers are easy to implement in SwiftUI and provide a clean way to present additional information or options without disrupting the user's flow. By using the &lt;code&gt;popover&lt;/code&gt; modifier, you can easily toggle the visibility of a popover and customize its content.&lt;/p&gt;</description>
  </item>
  <item>
    <title>Handle plurals in Swift with inflection</title>    <link>https://wesleydegroot.nl/blog/handle-plurals-in-swift-with-inflection</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/handle-plurals-in-swift-with-inflection</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:24 +0200</pubDate>
    <category>SwiftUI</category>
    <description>&lt;p&gt;Inflection is a powerful tool in Swift that allows developers to handle plurals and other linguistic variations in a more elegant way. This post will explore how to use inflection in Swift, providing examples and best practices.&lt;/p&gt;
&lt;h2&gt;What is Inflection?&lt;/h2&gt;
&lt;p&gt;a change in the form of a word (typically the ending) to express a grammatical function or attribute such as tense, mood, person, number, case, and gender.&lt;br&gt;
Inflection in Swift allows you to automatically adjust the form of words based on context, such as singular or plural forms. This is particularly useful in user interfaces where you want to display text that changes based on user input or data.&lt;/p&gt;
&lt;h2&gt;Code&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct BookCollection: View {
    @State private var bookCount: Int = 0

    var body: some View {
        VStack {
            Text("You've bought ^[\(bookCount) book](inflect: true) this year!")

            Button("Add a book") {
                bookCount += 1
            }
        }
    }
}

#Preview {
    BookCollection()
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, we create a simple SwiftUI view that displays the number of books purchased. The &lt;code&gt;Text&lt;/code&gt; view uses an inflection feature to automatically handle the pluralization of the word "book" based on the value of &lt;code&gt;bookCount&lt;/code&gt;. When &lt;code&gt;bookCount&lt;/code&gt; is 1, it will display "1 book", and for any other number, it will display the correct plural form "books".&lt;br&gt;
This approach makes it easy to manage plurals without having to write additional logic or conditional statements, leading to cleaner and more maintainable code.&lt;/p&gt;
&lt;h2&gt;Supported languages&lt;/h2&gt;
&lt;p&gt;Inflection supports (as of writing this article) the following languages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;English&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;French&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;German&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Hindi&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Italian&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Korean&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Portuguese&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Spanish&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Inflection in Swift is a powerful feature that simplifies the handling of plurals and other linguistic variations in your applications. By using inflection, you can create more dynamic and user-friendly interfaces without cluttering your code with complex logic. This not only enhances the user experience but also improves code readability and maintainability.&lt;/p&gt;</description>
  </item>
  <item>
    <title>CocoaPods Trunk Read-only Plan</title>    <link>https://wesleydegroot.nl/blog/cocoapods-trunk-read-only-plan</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/cocoapods-trunk-read-only-plan</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:25 +0200</pubDate>
    <category>CocoaPods</category>
    <description>&lt;p&gt;CocoaPods Trunk Read-only Plan&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;CocoaPods&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;CocoaPods is a dependency manager for Swift and Objective-C Cocoa projects. It has thousands of libraries and can help you scale your projects elegantly.&lt;br&gt;
It is used to manage library dependencies in iOS and macOS applications, allowing developers to easily integrate third-party libraries into their projects.&lt;br&gt;
It is widely used in the iOS development community to simplify the process of managing external libraries and frameworks.&lt;/p&gt;
&lt;h2&gt;What will happen to &lt;code&gt;CocoaPods&lt;/code&gt; pods?&lt;/h2&gt;
&lt;p&gt;CocoaPods will continue to function as a dependency manager, but the Trunk service will be read-only. This means that while you can still use CocoaPods to manage your dependencies, you will not be able to publish new pods or update existing ones through the Trunk service.&lt;/p&gt;
&lt;h2&gt;Migration to &lt;code&gt;Swift Package Manager&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The CocoaPods team is encouraging developers to migrate their projects to use the Swift Package Manager (SPM) for dependency management. SPM is Apple's official package manager for Swift and is integrated into Xcode, making it a more modern and streamlined solution for managing dependencies in Swift projects.&lt;/p&gt;
&lt;h2&gt;When will this happen?&lt;/h2&gt;
&lt;p&gt;This will happen on 02-DEC-2026, This is a Wednesday after American Thanksgiving.&lt;/p&gt;
&lt;h2&gt;More Information&lt;/h2&gt;
&lt;p&gt;For more information, you can refer to the official announcement on the CocoaPods blog: &lt;a href="https://blog.cocoapods.org/CocoaPods-Trunk-Read-only-Plan/"&gt;CocoaPods Trunk Read-only Plan&lt;/a&gt;&lt;/p&gt;</description>
  </item>
  <item>
    <title>Thermal States on iOS</title>    <link>https://wesleydegroot.nl/blog/thermal-states-on-ios</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/thermal-states-on-ios</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:25 +0200</pubDate>
    <category>iOS</category>
    <category>Swift</category>
    <description>&lt;p&gt;You can read the thermal states on iOS documentation to understand how to manage the thermal state of your iOS application. This is important for optimizing performance and battery life.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;Thermal State&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;Thermal states on iOS refer to the different levels of thermal conditions that an iOS device can experience. These states are used by the system to manage performance and power consumption based on the device's temperature. The thermal state can affect how your application behaves, especially in terms of CPU and GPU usage.&lt;/p&gt;
&lt;h2&gt;Reasons to know the &lt;code&gt;Thermal State&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;For example if you want to compress a video, you might want to know the thermal state of the device to avoid overheating. If the device is in a high thermal state, you might want to reduce the quality of the video compression or pause it to prevent the device from overheating.&lt;/p&gt;
&lt;h2&gt;How to read the &lt;code&gt;Thermal State&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;You can read the thermal state of an iOS device using the &lt;code&gt;ProcessInfo&lt;/code&gt; class. The &lt;code&gt;thermalState&lt;/code&gt; property of &lt;code&gt;ProcessInfo&lt;/code&gt; provides the current thermal state of the device. You can use this information to adjust your application's behavior based on the thermal conditions.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import Foundation

let thermalState = ProcessInfo.processInfo.thermalState

switch thermalState {
case .nominal:
    print("Device is in a nominal thermal state.")
case .fair:
    print("Device is in a fair thermal state.")
case .serious:
    print("Device is in a serious thermal state.")
case .critical:
    print("Device is in a critical thermal state.")
case .unknown:
    print("Device thermal state is unknown.")
default:
    print("Device thermal state is not recognized.")
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Example for video compression&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import Foundation
var videoCompressionInProgress = true

func shouldContinueVideoCompression() -&amp;gt; Bool {
    let thermalState = ProcessInfo.processInfo.thermalState

    switch thermalState {
    case .nominal, .fair:
        // Continue compression at normal quality
        return true
    case .serious, .critical:
        // Reduce quality or pause compression to prevent overheating
        return false
    case .unknown:
        // Handle unknown state cautiously
        return false
}

func compressFrame() {
    // Your video compression logic here
    print("Compressing video frame...")
}

while (shouldContinueVideoCompression() &amp;amp;&amp;amp; videoCompressionInProgress) {
    compressFrame() // Your video compression logic here
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Knowing the thermal state of the device is curcial if you want to execute work which can cause the device to overheat. By using the &lt;code&gt;ProcessInfo&lt;/code&gt; class, you can read the thermal state and adjust your application's behavior accordingly. This can help you optimize performance and battery life, ensuring a better user experience.&lt;/p&gt;</description>
  </item>
  <item>
    <title>Move your app to the background</title>    <link>https://wesleydegroot.nl/blog/move-your-app-to-the-background</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/move-your-app-to-the-background</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:26 +0200</pubDate>
    <category>iOS</category>
    <description>&lt;p&gt;In this (small) post, we will explore how to move your iOS application to the background programmatically.&lt;br&gt;
This is offcially &lt;strong&gt;not&lt;/strong&gt; supported by Apple.&lt;/p&gt;
&lt;h2&gt;Send app to background&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ContentView: View {
    var body: some View {
        Button("Send App to Background") {
            UIControl()
                .sendAction(
                    #selector(URLSessionTask.suspend), 
                    to: UIApplication.shared, 
                    for: nil
                )
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;What is &lt;code&gt;UIControl&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicontrol"&gt;&lt;code&gt;UIControl&lt;/code&gt;&lt;/a&gt; is a class in UIKit that provides the base functionality for controls such as buttons, switches, and sliders. It is used to handle user interactions and events in iOS applications.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;sendAction&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uicontrol/sendaction(_:to:for:)"&gt;&lt;code&gt;sendAction(_:to:for:)&lt;/code&gt;&lt;/a&gt; is a method of &lt;a href="https://developer.apple.com/documentation/uikit/uicontrol"&gt;&lt;code&gt;UIControl&lt;/code&gt;&lt;/a&gt; that sends an action message to the target object. It allows you to trigger a specific action when a control is interacted with, such as tapping a button.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;URLSessionTask.suspend&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;a href='https://developer.apple.com/documentation/foundation/urlsessiontask/suspend()'&gt;&lt;code&gt;URLSessionTask.suspend&lt;/code&gt;&lt;/a&gt; is a method that suspends the task, effectively pausing any ongoing network operations.&lt;br&gt;
However in this context, it is used to send the app to the background by invoking this method on the shared &lt;code&gt;UIApplication&lt;/code&gt; instance.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;UIApplication.shared&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uiapplication/shared"&gt;&lt;code&gt;UIApplication.shared&lt;/code&gt;&lt;/a&gt; is a singleton instance of the &lt;code&gt;UIApplication&lt;/code&gt; class that represents the current application. It provides access to various application-level properties and methods, including managing the app's lifecycle and handling events.&lt;/p&gt;
&lt;h2&gt;How does it work?&lt;/h2&gt;
&lt;p&gt;The code snippet provided uses the &lt;a href="https://developer.apple.com/documentation/uikit/uicontrol/sendaction(_:to:for:)"&gt;&lt;code&gt;sendAction(_:to:for:)&lt;/code&gt;&lt;/a&gt; method of &lt;a href="https://developer.apple.com/documentation/uikit/uicontrol"&gt;&lt;code&gt;UIControl&lt;/code&gt;&lt;/a&gt; to send an action to the &lt;a href="https://developer.apple.com/documentation/uikit/uiapplication/shared"&gt;&lt;code&gt;UIApplication.shared&lt;/code&gt;&lt;/a&gt; instance, specifically calling the &lt;a href='https://developer.apple.com/documentation/foundation/urlsessiontask/suspend()'&gt;&lt;code&gt;suspend&lt;/code&gt;&lt;/a&gt; method of &lt;a href='https://developer.apple.com/documentation/foundation/urlsessiontask/'&gt;&lt;code&gt;URLSessionTask&lt;/code&gt;&lt;/a&gt;. This effectively moves the app to the background.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This method is not officially supported by Apple and should be used with caution. It is generally recommended to let the system manage the app's lifecycle and background behavior. However, if you need to programmatically move your app to the background, this approach can be used.&lt;/p&gt;</description>
  </item>
  <item>
    <title>Using the share sheet to share content</title>    <link>https://wesleydegroot.nl/blog/using-the-share-sheet-to-share-content</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/using-the-share-sheet-to-share-content</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:26 +0200</pubDate>
    <category>SwiftUI</category>
    <description>&lt;p&gt;In this post, we will explore how to use the share sheet in SwiftUI to share content from your app. The share sheet allows users to share text, images, URLs, and other types of content with other apps or services on their device.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;ShareLink&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/sharelink"&gt;&lt;code&gt;ShareLink&lt;/code&gt;&lt;/a&gt; is a view in SwiftUI that provides a way to share content using the system's share sheet. It allows you to specify the content you want to share, such as text, images, or URLs, and presents the share sheet when the user interacts with it.&lt;/p&gt;
&lt;h2&gt;Different ways to use &lt;code&gt;ShareLink&lt;/code&gt;&lt;/h2&gt;
&lt;h3&gt;ShareLink without title&lt;/h3&gt;
&lt;p&gt;You can create a &lt;code&gt;ShareLink&lt;/code&gt; without a title, which will use the default system-provided title for the share action.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;ShareLink(
    item: URL(string: "https://wesleydegroot.nl")!
)&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;ShareLink with customized link title&lt;/h3&gt;
&lt;p&gt;You can customize the title of the share link by providing a string as the first argument.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;ShareLink(
    "Share Website",
    item: URL(string: "https://wesleydegroot.nl")!
)&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;ShareLink with a customized sharing subject and message&lt;/h3&gt;
&lt;p&gt;You can also customize the subject and message of the share action by providing &lt;code&gt;Text&lt;/code&gt; views for the &lt;code&gt;subject&lt;/code&gt; and &lt;code&gt;message&lt;/code&gt; parameters.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;ShareLink(
    item: URL(string: "https://wesleydegroot.nl")!,
    subject: Text("Share blog"),
    message: Text("Share blog post ShareLink")
)&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;ShareLink with a preview&lt;/h3&gt;
&lt;p&gt;You can provide a preview for the shared content using the &lt;code&gt;preview&lt;/code&gt; parameter. This allows you to specify a title and an icon for the preview.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;ShareLink(
    item: URL(string: "https://appsterdam.rs")!,
    preview: SharePreview(
        "Join Appsterdam!",
        icon: Image(systemName: "person.3.sequence.fill")
    )
)&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;ShareLink with a custom label&lt;/h3&gt;
&lt;p&gt;You can create a &lt;code&gt;ShareLink&lt;/code&gt; with a custom label by providing a closure that returns a view, such as a &lt;code&gt;Label&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;ShareLink(item: URL(string: "https://wesleydegroot.nl")!) {
    Label(
        "Share my website",
        systemImage: "star"
    )
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;ShareLink with Transferable object&lt;/h3&gt;
&lt;p&gt;You can also share custom objects that conform to the &lt;code&gt;Transferable&lt;/code&gt; protocol. This allows you to share complex data types, such as images or structured data.&lt;/p&gt;
&lt;h4&gt;Example of a Transferable object&lt;/h4&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct BlogPost: Transferable {
    static var transferRepresentation: some TransferRepresentation {
        ProxyRepresentation(exporting: \.image)
    }

    public var image: Image
    public var title: String
    public var description: String
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Using ShareLink with a Transferable object&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;ShareLink(
    item: post,
    preview: SharePreview(post.title, image: post.image)
)&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Full code example&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ShareLinkDemoView: View {
    let post: BlogPost = .init(
        image: Image(systemName: "star"),
        title: "Welcome to this blog post",
        description: "With a description that is longer than the title"
    )

    var body: some View {
        List {
            Section("ShareLink without title") {
                ShareLink(
                    item: URL(string: "https://wesleydegroot.nl")!
                )
            }

            Section("ShareLink with customized link title") {
                ShareLink(
                    "Share Website",
                    item: URL(string: "https://wesleydegroot.nl")!
                )
            }

            Section("ShareLink with a customized sharing subject and message") {
                ShareLink(
                    item: URL(string: "https://wesleydegroot.nl")!,
                    subject: Text("Share blog"),
                    message: Text("Share blog post ShareLink")
                )
            }

            Section("ShareLink with a preview") {
                ShareLink(
                    item: URL(string: "https://appsterdam.rs")!,
                    preview: SharePreview(
                        "Join Appsterdam!",
                        icon: Image(systemName: "person.3.sequence.fill")
                    )
                )
            }

            Section("ShareLink with a custom label") {
                ShareLink(item: URL(string: "https://wesleydegroot.nl")!) {
                    Label(
                        "Share my website",
                        systemImage: "star"
                    )
                }
            }

            Section("ShareLink with Transferable object") {
                ShareLink(
                    item: post,
                    preview: SharePreview(post.title, image: post.image)
                )
            }
        }
    }
}

struct BlogPost: Transferable {
    static var transferRepresentation: some TransferRepresentation {
        ProxyRepresentation(exporting: \.image)
    }

    public var image: Image
    public var title: String
    public var description: String
}

#Preview {
    ShareLinkDemoView()
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Screenshots&lt;/h2&gt;
&lt;table&gt;
    &lt;tr&gt;
        &lt;td&gt;
        &lt;img src="../resources/ShareLink1.png" alt="ShareLink Demo 2"&gt;
        &lt;/td&gt;
        &lt;td&gt;
        &lt;img src="../resources/ShareLink2.png" alt="ShareLink Demo 2"&gt;
        &lt;/td&gt;
    &lt;/tr&gt;
&lt;/table&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;ShareLink is a powerful and flexible way to share content in SwiftUI applications. It allows you to easily present the system's share sheet and customize the content being shared. By using ShareLink, you can enhance the user experience of your app by enabling users to share content with their favorite apps and services seamlessly.&lt;/p&gt;</description>
  </item>
  <item>
    <title>Gradients in text using foregroundStyle</title>    <link>https://wesleydegroot.nl/blog/gradients-in-text-using-foregroundstyle</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/gradients-in-text-using-foregroundstyle</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:27 +0200</pubDate>
    <category>SwiftUI</category>
    <description>&lt;p&gt;In this post, we will explore how to create gradients in text using the &lt;code&gt;foregroundStyle&lt;/code&gt; modifier in SwiftUI. This technique allows for visually appealing text effects that can enhance the user interface of your applications.&lt;/p&gt;
&lt;h2&gt;What is &lt;a href="https://developer.apple.com/documentation/swiftui/text/foregroundstyle%28_%3A%29"&gt;&lt;code&gt;.foreGroundStyle&lt;/code&gt;&lt;/a&gt;?&lt;/h2&gt;
&lt;p&gt;ForegroundStyle is a SwiftUI modifier that allows you to apply a style to the foreground of a view, such as text. It can be used to create gradients, patterns, or other visual effects that enhance the appearance of text elements.&lt;/p&gt;
&lt;h2&gt;Usage&lt;/h2&gt;
&lt;p&gt;To use &lt;code&gt;foregroundStyle&lt;/code&gt;, you can apply it to a &lt;code&gt;Text&lt;/code&gt; view in SwiftUI. Here’s a simple example:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ContentView: View {
    var body: some View {
        Text("Hello, SwiftUI!")
            .font(.largeTitle)
            .foregroundStyle(
                LinearGradient(
                    gradient: Gradient(colors: [.red, .blue]),
                    startPoint: .leading,
                    endPoint: .trailing
                )
            )
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Old way (without &lt;code&gt;foregroundStyle&lt;/code&gt; and a mask)&lt;/h2&gt;
&lt;p&gt;This is the traditional way of applying a gradient to text before the introduction of &lt;code&gt;foregroundStyle&lt;/code&gt;. It involves using a mask to apply the gradient to the text.&lt;br&gt;
Note: This does not work well with Emoji.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct OldContentView: View {
    var body: some View {
        Text("Hello, SwiftUI!")
            .font(.largeTitle)
            .overlay {
                LinearGradient(
                    colors: colors,
                    startPoint: startPoint,
                    endPoint: endPoint
                )
                .mask(
                    self
                )
            }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Combining the two approaches (for supporting older iOS versions)&lt;/h2&gt;
&lt;p&gt;To support older iOS versions that do not have the &lt;code&gt;foregroundStyle&lt;/code&gt; modifier, you can combine both approaches. This way, you can use &lt;code&gt;foregroundStyle&lt;/code&gt; on newer versions and fall back to the masking technique on older versions.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

extension Text {
    /// Make the foreground gradient
    ///
    /// Create a gradient for the foreground of the text.
    ///
    /// - Parameters:
    ///   - colors: Colors
    ///   - startPoint: Startpoint
    ///   - endPoint: End point
    /// - Returns: self
    @ViewBuilder
    public func foregroundLinearGradient(
        colors: [Color] = [.red, .blue, .green, .yellow],
        startPoint: UnitPoint = .leading,
        endPoint: UnitPoint = .trailing
    ) -&amp;gt; some View {
        if #available(iOS 17, *) {
            self.foregroundStyle(
                LinearGradient(
                    colors: colors,
                    startPoint: startPoint,
                    endPoint: endPoint
                )
            )
        } else {
            self.overlay {
                LinearGradient(
                    colors: colors,
                    startPoint: startPoint,
                    endPoint: endPoint
                )
                .mask(
                    self
                )
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;foregroundStyle&lt;/code&gt; modifier in SwiftUI provides a powerful way to apply gradients and other styles to text. This enhances the visual appeal of your applications and allows for creative text effects. By using &lt;code&gt;LinearGradient&lt;/code&gt;, &lt;code&gt;RadialGradient&lt;/code&gt;, or &lt;code&gt;AngularGradient&lt;/code&gt;, you can easily create stunning text designs that stand out in your user interface.&lt;/p&gt;</description>
  </item>
  <item>
    <title>Difference between animations in SwiftUI</title>    <link>https://wesleydegroot.nl/blog/difference-between-animations-in-swiftui</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/difference-between-animations-in-swiftui</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:28 +0200</pubDate>
    <category>SwiftUI</category>
    <category>Animations</category>
    <description>&lt;p&gt;In this post, we will explore the difference between &lt;a href="https://developer.apple.com/documentation/swiftui/view/animation(_:)"&gt;&lt;code&gt;.animation()&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://developer.apple.com/documentation/swiftui/withanimation(_:_:)"&gt;&lt;code&gt;withAnimation()&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://developer.apple.com/documentation/swiftui/view/phaseanimator(_:content:animation:)"&gt;&lt;code&gt;phaseAnimator()&lt;/code&gt;&lt;/a&gt; in SwiftUI. Both are used to create animations, but they serve different purposes and have different use cases.&lt;/p&gt;
&lt;h2&gt;What is &lt;a href="https://developer.apple.com/documentation/swiftui/view/animation(_:)"&gt;&lt;code&gt;.animation()&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://developer.apple.com/documentation/swiftui/withanimation(_:_:)"&gt;&lt;code&gt;withAnimation()&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://developer.apple.com/documentation/swiftui/phaseanimation"&gt;&lt;code&gt;phaseAnimation()&lt;/code&gt;&lt;/a&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/animation(_:)"&gt;&lt;code&gt;.animation()&lt;/code&gt;&lt;/a&gt; is a view modifier in SwiftUI that applies an animation to changes in a view's state. It can be used to animate specific properties of a view, such as its position, scale, or opacity, when those properties change.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/withanimation(_:_:)"&gt;&lt;code&gt;withAnimation()&lt;/code&gt;&lt;/a&gt; is a function that allows you to perform state changes with an animation. It can be used to wrap any state-changing code, and the changes will be animated automatically.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/phaseanimator(_:content:animation:)"&gt;&lt;code&gt;phaseAnimator()&lt;/code&gt;&lt;/a&gt; is a more advanced animation API that allows you to create complex animations by defining different phases of an animation and transitioning between them. It provides greater control over the animation process and can be used for more intricate animations.&lt;/p&gt;
&lt;p&gt;In summary, use &lt;a href="https://developer.apple.com/documentation/swiftui/view/animation(_:)"&gt;&lt;code&gt;.animation()&lt;/code&gt;&lt;/a&gt; when you want to apply an animation to a specific view, and use &lt;a href="https://developer.apple.com/documentation/swiftui/withanimation(_:_:)"&gt;&lt;code&gt;withAnimation()&lt;/code&gt;&lt;/a&gt; when you want to animate state changes more generally.&lt;/p&gt;
&lt;h2&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/animation(_:)"&gt;&lt;code&gt;.animation()&lt;/code&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;Example&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct ContentView: View {
    @State private var isScaled = false

    var body: some View {
        Circle()
            .scaleEffect(isScaled ? 1.5 : 1.0)
            .animation(.easeInOut(duration: 0.5))
            .onTapGesture {
                isScaled.toggle()
            }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When isScaled changes, SwiftUI checks if the value passed to .animation() has changed.&lt;br&gt;
If it has, the animation is applied to the changes in that view’s state.&lt;br&gt;
You’re essentially telling the view how it should animate as its state updates.&lt;/p&gt;
&lt;h3&gt;Pros of &lt;a href="https://developer.apple.com/documentation/swiftui/view/animation(_:)"&gt;&lt;code&gt;.animation()&lt;/code&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Simple and concise.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Declarative style fits right into SwiftUI’s architecture.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Great for chaining effects (e.g. scale, opacity, rotation).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Cons of &lt;a href="https://developer.apple.com/documentation/swiftui/view/animation(_:)"&gt;&lt;code&gt;.animation()&lt;/code&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Limited control over timing and grouping.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Only works on the specific view it's attached to.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Use Case: Animating a Button Press&lt;/h3&gt;
&lt;p&gt;In this example, we have a button that scales up when pressed and scales back down when released. We use &lt;a href="https://developer.apple.com/documentation/swiftui/view/animation(_:)"&gt;&lt;code&gt;.animation()&lt;/code&gt;&lt;/a&gt; to apply the scaling animation to the button's scale effect.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct ContentView: View {
    @State private var isActivated = false

    var body: some View {
        Button(action: {
            isActivated.toggle()
        }) {
            Text("Press Me")
                .padding()
                .background(Color.blue)
                .foregroundColor(.white)
                .cornerRadius(10)
                .scaleEffect(isActivated ? 1.2 : 1.0)
                .animation(.easeInOut(duration: 0.2))
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/withanimation(_:_:)"&gt;&lt;code&gt;withAnimation()&lt;/code&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/withanimation(_:_:)"&gt;&lt;code&gt;withAnimation()&lt;/code&gt;&lt;/a&gt; is a function that allows you to perform state changes with an animation. It can be used to wrap any state-changing code, and the changes will be animated automatically.&lt;/p&gt;
&lt;h3&gt;Example&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct ContentView: View {
    @State private var isVisible = false

    var body: some View {
        VStack {
            Button("Toggle Visibility") {
                withAnimation(.easeInOut(duration: 0.5)) {
                    isVisible.toggle()
                }
            }

            if isVisible {
                Text("Hello, World!")
                    .transition(.slide)
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, we use &lt;a href="https://developer.apple.com/documentation/swiftui/withanimation(_:_:)"&gt;&lt;code&gt;withAnimation()&lt;/code&gt;&lt;/a&gt; to wrap the state change of &lt;code&gt;isVisible&lt;/code&gt;. When the button is pressed, the text will appear or disappear with a sliding transition. The animation is applied to the entire state change, including the transition of the text.&lt;/p&gt;
&lt;h3&gt;Pros of &lt;a href="https://developer.apple.com/documentation/swiftui/withanimation(_:_:)"&gt;&lt;code&gt;withAnimation()&lt;/code&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Provides a simple way to animate state changes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Can be used with any state-changing code, not just view properties.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Allows for more complex animations by combining multiple state changes.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Cons of &lt;a href="https://developer.apple.com/documentation/swiftui/withanimation(_:_:)"&gt;&lt;code&gt;withAnimation()&lt;/code&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Less control over individual view animations.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Can lead to unexpected animations if not used carefully.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Use Case: Animating a View Transition&lt;/h3&gt;
&lt;p&gt;In this example, we use &lt;a href="https://developer.apple.com/documentation/swiftui/withanimation(_:_:)"&gt;&lt;code&gt;withAnimation()&lt;/code&gt;&lt;/a&gt; to wrap the state change of &lt;code&gt;isVisible&lt;/code&gt;. When the button is pressed, the text will appear or disappear with a sliding transition. The animation is applied to the entire state change, including the transition of the text.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct ContentView: View {
    @State private var isVisible = false

    var body: some View {
        VStack {
            Button("Toggle Visibility") {
                withAnimation(.easeInOut(duration: 0.5)) {
                    isVisible.toggle()
                }
            }

            if isVisible {
                Text("Hello, World!")
                    .transition(.slide)
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/phaseanimator(_:content:animation:)"&gt;&lt;code&gt;phaseAnimator()&lt;/code&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/phaseanimator(_:content:animation:)"&gt;&lt;code&gt;phaseAnimator()&lt;/code&gt;&lt;/a&gt; is a more advanced animation API that allows you to create complex animations by defining different phases of an animation and transitioning between them. It provides greater control over the animation process and can be used for more intricate animations.&lt;/p&gt;
&lt;h3&gt;Example&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ContentView: View {
    @State private var currentPhase: Int = 0

    var body: some View {
        PhaseAnimator([0, 1, 2]) { phase in
            Circle()
                .frame(width: phase == 0 ? 50 : (phase == 1 ? 100 : 150))
                .foregroundStyle(phase == 0 ? .blue : (phase == 1 ? .green : .red))
        } animation: { phase in
            switch phase {
            case 0: .easeIn(duration: 0.5)
            case 1: .spring(response: 0.6, dampingFraction: 0.8)
            default: .linear(duration: 0.3)
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This example uses &lt;a href="https://developer.apple.com/documentation/swiftui/view/phaseanimator(_:content:animation:)"&gt;&lt;code&gt;phaseAnimator()&lt;/code&gt;&lt;/a&gt; to create a circle that changes its size and color based on the current phase. The animation is defined for each phase, allowing for more complex transitions.&lt;/p&gt;
&lt;h2&gt;Pros of &lt;a href="https://developer.apple.com/documentation/swiftui/view/phaseanimator(_:content:animation:)"&gt;&lt;code&gt;phaseAnimator()&lt;/code&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Provides fine-grained control over the animation process.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Allows for complex animations with multiple phases.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Can be used to create intricate animations that are difficult to achieve with &lt;a href="https://developer.apple.com/documentation/swiftui/view/animation(_:)"&gt;&lt;code&gt;.animation()&lt;/code&gt;&lt;/a&gt; or &lt;a href="https://developer.apple.com/documentation/swiftui/withanimation(_:_:)"&gt;&lt;code&gt;withAnimation()&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Supports custom timing and easing functions for each phase.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Cons of &lt;a href="https://developer.apple.com/documentation/swiftui/view/phaseanimator(_:content:animation:)"&gt;&lt;code&gt;phaseAnimator()&lt;/code&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;More complex to set up compared to &lt;a href="https://developer.apple.com/documentation/swiftui/view/animation(_:)"&gt;&lt;code&gt;.animation()&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://developer.apple.com/documentation/swiftui/withanimation(_:_:)"&gt;&lt;code&gt;withAnimation()&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Requires a deeper understanding of SwiftUI's animation system.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Use Case: Creating a Complex Animation&lt;/h2&gt;
&lt;p&gt;In this example, we use &lt;a href="https://developer.apple.com/documentation/swiftui/view/phaseanimator(_:content:animation:)"&gt;&lt;code&gt;phaseAnimator()&lt;/code&gt;&lt;/a&gt; to create a circle that changes its size and color based on the current phase. The animation is defined for each phase, allowing for more complex transitions.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ContentView: View {
    @State private var phase: Int = 0

    var body: some View {
        PhaseAnimator([0, 1, 2]) { phase in
            Rectangle()
                .frame(width: phase == 0 ? 100 : 200, height: 50)
                .foregroundStyle(phase == 1 ? .blue : .red)
        } animation: { _ in
            .spring(response: 0.5, dampingFraction: 0.7)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Side-by-Side Comparison&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/animation(_:)"&gt;&lt;code&gt;.animation()&lt;/code&gt;&lt;/a&gt;&lt;/th&gt;
&lt;th&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/withanimation(_:_:)"&gt;&lt;code&gt;withAnimation()&lt;/code&gt;&lt;/a&gt;&lt;/th&gt;
&lt;th&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/phaseanimator(_:content:animation:)"&gt;&lt;code&gt;phaseAnimator()&lt;/code&gt;&lt;/a&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Purpose&lt;/td&gt;
&lt;td&gt;Animates specific view properties&lt;/td&gt;
&lt;td&gt;Animates state changes&lt;/td&gt;
&lt;td&gt;Provides fine-grained control over the animation process.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Usage&lt;/td&gt;
&lt;td&gt;View modifier for specific views&lt;/td&gt;
&lt;td&gt;Function to wrap state changes&lt;/td&gt;
&lt;td&gt;Used to create intricate animations with multiple phases.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Control&lt;/td&gt;
&lt;td&gt;Limited to the view it's attached to&lt;/td&gt;
&lt;td&gt;More control over state changes&lt;/td&gt;
&lt;td&gt;Supports custom timing and easing functions for each phase.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Timing&lt;/td&gt;
&lt;td&gt;Fixed timing for the view&lt;/td&gt;
&lt;td&gt;Customizable timing for state changes&lt;/td&gt;
&lt;td&gt;Allows for complex animations with multiple phases.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grouping&lt;/td&gt;
&lt;td&gt;Limited to the view's properties&lt;/td&gt;
&lt;td&gt;Can group multiple state changes&lt;/td&gt;
&lt;td&gt;Enables intricate animations that are difficult to achieve with &lt;code&gt;.animation()&lt;/code&gt; or &lt;code&gt;withAnimation()&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;When to Use Each&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Use &lt;a href="https://developer.apple.com/documentation/swiftui/view/animation(_:)"&gt;&lt;code&gt;.animation()&lt;/code&gt;&lt;/a&gt; when you want to animate specific properties of a view, such as its position, scale, or opacity.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use &lt;a href="https://developer.apple.com/documentation/swiftui/withanimation(_:_:)"&gt;&lt;code&gt;withAnimation()&lt;/code&gt;&lt;/a&gt; when you want to animate state changes more generally, such as showing or hiding views, or changing multiple properties at once.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use &lt;a href="https://developer.apple.com/documentation/swiftui/view/phaseanimator(_:content:animation:)"&gt;&lt;code&gt;phaseAnimator()&lt;/code&gt;&lt;/a&gt; when you need fine-grained control over the animation process, or when you want to create complex animations with multiple phases.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In summary, &lt;a href="https://developer.apple.com/documentation/swiftui/view/animation(_:)"&gt;&lt;code&gt;.animation()&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://developer.apple.com/documentation/swiftui/withanimation(_:_:)"&gt;&lt;code&gt;withAnimation()&lt;/code&gt;&lt;/a&gt; are both powerful tools for creating animations in SwiftUI, but they serve different purposes. Use &lt;a href="https://developer.apple.com/documentation/swiftui/view/animation(_:)"&gt;&lt;code&gt;.animation()&lt;/code&gt;&lt;/a&gt; when you want to apply an animation to a specific view, and use &lt;a href="https://developer.apple.com/documentation/swiftui/withanimation(_:_:)"&gt;&lt;code&gt;withAnimation()&lt;/code&gt;&lt;/a&gt; when you want to animate state changes more generally. For more complex animations, consider using &lt;a href="https://developer.apple.com/documentation/swiftui/view/phaseanimator(_:content:animation:)"&gt;&lt;code&gt;phaseAnimator()&lt;/code&gt;&lt;/a&gt;, which provides greater control over the animation process and allows for intricate animations with multiple phases.&lt;/p&gt;</description>
  </item>
  <item>
    <title>Adjust the intensity of colors in SwiftUI views</title>    <link>https://wesleydegroot.nl/blog/adjust-the-intensity-of-colors-in-swiftui-views</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/adjust-the-intensity-of-colors-in-swiftui-views</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:28 +0200</pubDate>
    <category>SwiftUI</category>
    <category>Colors</category>
    <category>Brightness</category>
    <description>&lt;p&gt;SwiftUI provides a powerful way to create dynamic and visually appealing user interfaces. One of the features that can enhance the visual experience is the ability to adjust the intensity of colors in views. This can be achieved using the &lt;code&gt;.brightness&lt;/code&gt; modifier, which allows developers to manipulate the brightness of colors in SwiftUI views.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;.brightness&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;.brightness&lt;/code&gt; modifier in SwiftUI is used to adjust the brightness of a view. It takes a value between -1.0 and 1.0, where -1.0 represents the darkest possible state and 1.0 represents the brightest. This modifier can be particularly useful for creating dynamic interfaces that respond to user preferences or environmental conditions.&lt;/p&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;p&gt;To use the &lt;code&gt;.brightness&lt;/code&gt; modifier, you do not need to install any additional libraries as it is part of the SwiftUI framework. Simply import SwiftUI in your project and you can start using it.&lt;/p&gt;
&lt;h2&gt;Use Case: Adjusting Color Intensity&lt;/h2&gt;
&lt;p&gt;In this example, we will demonstrate how to use the &lt;code&gt;.brightness&lt;/code&gt; modifier to adjust the intensity of colors in a SwiftUI view. This can be particularly useful for creating themes or adjusting the appearance of views based on user preferences.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

// Example of using the .brightness modifier in SwiftUI
// This example creates a series of colored views with varying brightness levels.
// it is everytime brighter than the previous one.
struct ContentView: View {
    var body: some View {
        ForEach(0..&amp;lt;10) { num in
            Color
                .purple
                .brightness(Double(num) * 0.1)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;details&gt;
&lt;summary&gt;Example of using the .brightness modifier in SwiftUI&lt;/summary&gt;
&lt;p&gt;&lt;img src="/resources/BrightnessDemoLight.png" alt="Example of using the .brightness modifier in SwiftUI" loading="lazy"&gt;&lt;/p&gt;
&lt;/details&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

// Example of using the .brightness modifier in SwiftUI
// This example creates a series of colored views with varying brightness levels.
// it is everytime darker than the previous one.
struct ContentView: View {
    var body: some View {
        ForEach(0..&amp;lt;10) { num in
            Color
                .purple
                .brightness(Double(num) * -0.1)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;details&gt;
&lt;summary&gt;Example of using the .brightness modifier in SwiftUI&lt;/summary&gt;
&lt;p&gt;&lt;img src="/resources/BrightnessDemoDark.png" alt="Example of using the .brightness modifier in SwiftUI" loading="lazy"&gt;&lt;/p&gt;
&lt;/details&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;p&gt;While the &lt;code&gt;.brightness&lt;/code&gt; modifier is powerful, there are a few caveats to keep in mind:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Accessibility: Ensure that the adjusted colors remain accessible to all users, including those with visual impairments. Test your views with different brightness levels to ensure readability and usability.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Device Variability: The appearance of colors can vary across different devices and screen types. Always test your views on multiple devices to ensure consistent appearance.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;In this article, we explored the &lt;code&gt;.brightness&lt;/code&gt; modifier in SwiftUI and how it can be used to adjust the intensity of colors in views. We discussed its use cases, implementation details, and potential caveats to keep in mind.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;.brightness&lt;/code&gt; modifier is a powerful tool for creating dynamic and visually appealing SwiftUI views. By understanding how to use it effectively, you can enhance the user experience in your applications.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/brightness(_"&gt;https://developer.apple.com/documentation/swiftui/view/brightness(_&lt;/a&gt;:)&lt;/p&gt;</description>
  </item>
  <item>
    <title>SwiftUI Buttons</title>    <link>https://wesleydegroot.nl/blog/swiftui-buttons</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/swiftui-buttons</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:29 +0200</pubDate>
    <category>SwiftUI</category>
    <category>Button</category>
    <description>&lt;p&gt;In this post we will explore the different styles of buttons available in SwiftUI, how to create custom buttons, and some best practices for using buttons in your SwiftUI applications.&lt;/p&gt;
&lt;h2&gt;What are Buttons in SwiftUI?&lt;/h2&gt;
&lt;p&gt;Buttons in SwiftUI are interactive elements that trigger actions when tapped. They can be styled and customized to fit the design of your application. SwiftUI provides a variety of built-in button styles, and you can also create your own custom styles.&lt;/p&gt;
&lt;h2&gt;Basic Button Usage&lt;/h2&gt;
&lt;p&gt;To create a basic button in SwiftUI, you can use the &lt;code&gt;Button&lt;/code&gt; view. Here’s a simple example:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;Button("My Button") {
    print("Button tapped!")
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Built-in Button Styles&lt;/h2&gt;
&lt;p&gt;SwiftUI provides several built-in button styles that you can use to change the appearance of your buttons.&lt;br&gt;
&lt;img src="/resources/swiftui_button_styles.png" alt="The different button styles in SwiftUI" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;Here are some common styles:&lt;/p&gt;
&lt;h3&gt;Default Button Style&lt;/h3&gt;
&lt;p&gt;The default button style is simple and adapts to the platform's design guidelines.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;Button("Default Button") {
    print("Default button tapped!")
}
.buttonStyle(DefaultButtonStyle())&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Borderless Button Style&lt;/h3&gt;
&lt;p&gt;The borderless button style removes the border and background, making it suitable for text-only buttons.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;Button("Borderless Button") {
    print("Borderless button tapped!")
}
.buttonStyle(BorderlessButtonStyle())&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Plain Button Style&lt;/h3&gt;
&lt;p&gt;The plain button style is used for buttons that do not have any background or border, making them look like regular text.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;Button("Plain Button") {
    print("Plain button tapped!")
}
.buttonStyle(PlainButtonStyle())&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Bordered Button Style&lt;/h3&gt;
&lt;p&gt;The bordered button style adds a border around the button, making it stand out more.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;Button("Bordered Button") {
    print("Bordered button tapped!")
}
.buttonStyle(BorderedButtonStyle())&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Bordered Prominent Button Style&lt;/h3&gt;
&lt;p&gt;The bordered prominent button style is similar to the bordered style but with a more pronounced appearance, often used for primary actions.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;Button("Bordered Prominent Button") {
    print("Bordered prominent button tapped!")
}
.buttonStyle(BorderedProminentButtonStyle())&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Button with (SF) Symbols&lt;/h2&gt;
&lt;p&gt;You can also add icons to your buttons using (SF) Symbols.&lt;br&gt;
This enhances the visual appeal and provides context to the button's action.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;Button {
    print("Button with icon tapped!")
} label: {
    Label("Button with Icon", systemImage: "star.fill")
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Use Case: Custom Button Styles&lt;/h2&gt;
&lt;p&gt;In this example, we will create a custom button style that changes the appearance of the button when tapped.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct CustomButtonStyle: ButtonStyle {
    @Environment(\.isEnabled) var isEnabled

    func makeBody(configuration: Configuration) -&amp;gt; some View {
        configuration.label
            .padding()
            // Change the background color to green when pressed, blue otherwise
            // and gray when disabled.
            .background(isEnabled ? (configuration.isPressed ? Color.green : Color.blue) : Color.gray)
            .foregroundColor(.white)
            .clipShape(Capsule())
    }
}

Button("Custom Button") {
    print("Custom button tapped!")
}
.buttonStyle(CustomButtonStyle())&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Best Practices for Using Buttons&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Accessibility&lt;/strong&gt;: Always ensure that buttons are accessible. Use descriptive labels and consider the size of the button for touch targets.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Feedback&lt;/strong&gt;: Provide visual feedback when a button is tapped. This can be done using animations or changing the button style.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Consistency&lt;/strong&gt;: Use consistent styles for buttons throughout your application to create a cohesive user experience.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Avoid Overloading&lt;/strong&gt;: Don’t overload buttons with too many actions. Each button should have a clear and singular purpose.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Buttons are a fundamental part of user interaction in SwiftUI applications. By understanding how to use and customize buttons, you can enhance the usability and aesthetics of your app. Remember to follow best practices to ensure that your buttons are accessible, provide feedback, and maintain consistency across your application.&lt;/p&gt;</description>
  </item>
  <item>
    <title>Surprise Route</title>    <link>https://wesleydegroot.nl/blog/surprise-route</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/surprise-route</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:29 +0200</pubDate>
    <category>Swift</category>
    <description>&lt;p&gt;In this post, I'll take you tough the process of creating my &lt;a href="/apps/Surprise-Route"&gt;Surprise Route&lt;/a&gt; app in Swift(UI).&lt;br&gt;
This app is designed to generate random routes for walking or biking, making your daily exercise routine more exciting and adventurous.&lt;/p&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;h3&gt;What is &lt;a href="/apps/Surprise-Route"&gt;Surprise Route&lt;/a&gt;?&lt;/h3&gt;
&lt;p&gt;&lt;a href="/apps/Surprise-Route"&gt;Surprise Route&lt;/a&gt; is an app that generates a random route for you to explore, making your daily walks or runs more exciting. It takes your current location and creates a unique path that you can follow, ensuring you discover new areas and enjoy the journey.&lt;/p&gt;
&lt;h3&gt;Why &lt;a href="/apps/Surprise-Route"&gt;Surprise Route&lt;/a&gt;?&lt;/h3&gt;
&lt;p&gt;Since a couple of weeks I'll try to walk or bike every day. I noticed that I often take the same routes, which can become monotonous. I wanted to create an app that would help me break out of this routine and explore new places in my neighborhood. That's how the idea for Surprise Route was born.&lt;/p&gt;
&lt;h2&gt;How does it work?&lt;/h2&gt;
&lt;p&gt;The app uses the current location and generates a random route by selecting random points within a certain radius. Then it asks to MapKit to create a path that connects these points, ensuring that the route is walkable or bikeable. The app also allows you to customize the distance and difficulty level of the route, making it suitable for different users.&lt;/p&gt;
&lt;h2&gt;Design&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-plain"&gt;┌────────────────────────────────────────┐
| ╭────────────────────────────────────╮ |
| |          (Surprise Route)      (i) | |
| |                                    | |
| |              MAP VIEW              | |
| |              MAP VIEW              | |
| |              MAP VIEW              | |
| |              MAP VIEW              | |
| |              MAP VIEW              | |
| |              MAP VIEW              | |
| |              MAP VIEW              | |
| |              MAP VIEW              | |
| |              MAP VIEW              | |
| |              MAP VIEW              | |
| |              MAP VIEW              | |
| |              MAP VIEW              | |
| |              MAP VIEW              | |
| ╰────────────────────────────────────╯ |
├────────────────────────────────────────┤
| Generate a [walk]↕ route for [1 hour]↕ |
| ┌────────────────────────────────────┐ |
| | Distance    Duration    Waypoints  | |
| | 5 km        1 hour      9          | |
| └────────────────────────────────────┘ |
| ╭────────────────────────────────────╮ |
| |         ↗ Generate Route           | |
| ╰────────────────────────────────────╯ |
└────────────────────────────────────────┘&lt;/code&gt;&lt;/pre&gt;
&lt;details&gt;
&lt;a name='screenshot'&gt;&lt;/a&gt;
&lt;summary&gt;Final design of the app&lt;/summary&gt;
&lt;img src='/assets/screenshots/surpriseroute_walk.png' alt='Surprise Route Walk'&gt;
&lt;/details&gt;
&lt;h2&gt;Encountered challenges&lt;/h2&gt;
&lt;p&gt;Sometimes routes generate a "spike" to one street to enter that street and then return to the original path. This can happen due to the way the random points are selected or how the path is generated. I'm currently working on improving the algorithm to minimize these spikes and create smoother routes.&lt;br&gt;
(see the screenshot in the details section above "&lt;a href='#screenshot' onClick='document.querySelector("details").open=true;'&gt;Final design of the app&lt;/a&gt;")&lt;/p&gt;
&lt;h2&gt;Fun facts&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The map is displayed using the UIKit version of MapKit, which allows to add overlays and annotations to the map. This makes it easy to visualize the route and see the points of interest along the way.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You can see where the waypoints are located on the map, go to ⓘ scroll to the bottom of the screen and enable "Show Waypoints". This helps you understand how the route is constructed.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;In this post, we explored the process of creating the &lt;a href="/apps/Surprise-Route"&gt;Surprise Route&lt;/a&gt; app in Swift(UI). We discussed its purpose, functionality, and the challenges faced during development. By leveraging the power of Swift and MapKit, we were able to create an app that encourages exploration and makes daily walks or runs more exciting.&lt;/p&gt;</description>
  </item>
  <item>
    <title>SwiftUI Lists</title>    <link>https://wesleydegroot.nl/blog/swiftui-lists</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/swiftui-lists</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:30 +0200</pubDate>
    <category>SwiftUI</category>
    <category>List</category>
    <description>&lt;p&gt;Discover &lt;a href="https://developer.apple.com/documentation/swiftui/list"&gt;&lt;code&gt;List&lt;/code&gt;&lt;/a&gt; in SwiftUI, a powerful way to display collections of data in a scrollable format. In this post, we will explore how to create lists, customize their appearance, and handle user interactions.&lt;/p&gt;
&lt;h2&gt;What is &lt;a href="https://developer.apple.com/documentation/swiftui/list"&gt;&lt;code&gt;List&lt;/code&gt;&lt;/a&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/list"&gt;&lt;code&gt;List&lt;/code&gt;&lt;/a&gt; is a container that presents rows of data in a single-column layout. It can display static or dynamic content and supports various customization options, making it a versatile choice for presenting data in your SwiftUI applications.&lt;/p&gt;
&lt;h2&gt;Built-in List Styles&lt;/h2&gt;
&lt;p&gt;SwiftUI provides several built-in styles for lists, allowing you to change their appearance and behavior.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;List Style&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/liststyle/plain"&gt;&lt;code&gt;.plain&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A minimal style with no separators or grouping. Great for clean designs.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/liststyle/grouped"&gt;&lt;code&gt;.grouped&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Groups items into visually distinct sections, often used in settings views.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/liststyle/insetgrouped"&gt;&lt;code&gt;.insetGrouped&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Like grouped, but with inset padding on larger screens (iPad, macOS).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/liststyle/sidebar"&gt;&lt;code&gt;.sidebar&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Ideal for navigation sidebars. Collapsible groups are supported.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/liststyle/inset"&gt;&lt;code&gt;.inset&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Provides subtle padding and separator lines.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/liststyle/automatic"&gt;&lt;code&gt;.automatic&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Lets the system choose the best style for the context.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;I like to use the &lt;a href="https://developer.apple.com/documentation/swiftui/liststyle/grouped"&gt;&lt;code&gt;.grouped&lt;/code&gt;&lt;/a&gt; style for most of my lists, as it provides a good balance between aesthetics and usability.&lt;/p&gt;
&lt;h2&gt;Creating a Basic List&lt;/h2&gt;
&lt;p&gt;To create a basic list, you can use the &lt;a href="https://developer.apple.com/documentation/swiftui/list"&gt;&lt;code&gt;List&lt;/code&gt;&lt;/a&gt; view with an array of data. Here's a simple example:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ContentView: View {
    let items = ["Apple", "Banana", "Cherry", "Date", "Elderberry"]

    var body: some View {
        List(items, id: \.self) { item in
            Text(item)
        }
        .navigationTitle("Fruits")
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code creates a list of fruits, where each item is displayed as a &lt;code&gt;Text&lt;/code&gt; view. The &lt;code&gt;id: \.self&lt;/code&gt; parameter indicates that each item in the array is unique and can be used as an identifier.&lt;/p&gt;
&lt;h2&gt;Customizing List Appearance&lt;/h2&gt;
&lt;p&gt;You can customize the appearance of a list by applying modifiers. For example, you can change the font, add images, or apply background colors to each row:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ContentView: View {
    let items = ["Apple", "Banana", "Cherry", "Date", "Elderberry"]

    var body: some View {
        List(items, id: \.self) { item in
            Text(item)
                .font(.headline)
                .padding()
                .background(Color.yellow)
                .cornerRadius(8)
        }
        .navigationTitle("Fruits")
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This example applies a headline font, padding, a yellow background, and rounded corners to each item in the list.&lt;/p&gt;
&lt;h2&gt;Handling User Interactions&lt;/h2&gt;
&lt;p&gt;You can handle user interactions with list items by adding gestures or navigation links. For example, you&lt;br&gt;
can navigate to a detail view when an item is tapped:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI
struct ContentView: View {
    let items = ["Apple", "Banana", "Cherry", "Date", "Elderberry"]

    var body: some View {
        NavigationView {
            List(items, id: \.self) { item in
                NavigationLink(destination: Text("Details for \(item)")) {
                    Text(item)
                }
            }
            .navigationTitle("Fruits")
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, tapping on an item navigates to a detail view that displays the selected fruit's name.&lt;/p&gt;
&lt;h2&gt;Customizing List Rows&lt;/h2&gt;
&lt;p&gt;You can create custom row views by defining a separate view for each row. This allows you to create complex layouts within each list item. Here's an example of a custom row view:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct FruitRow: View {
    let fruit: String

    var body: some View {
        HStack {
            Text(fruit)
                .font(.headline)
            Spacer()
            Image(systemName: "chevron.right")
        }
        .padding()
    }
}
struct ContentView: View {
    let items = ["Apple", "Banana", "Cherry", "Date", "Elderberry"]

    var body: some View {
        NavigationView {
            List(items, id: \.self) { item in
                FruitRow(fruit: item)
            }
            .navigationTitle("Fruits")
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, the &lt;code&gt;FruitRow&lt;/code&gt; view is used to create a custom row layout with a horizontal stack (&lt;code&gt;HStack&lt;/code&gt;) that displays the fruit name and a chevron icon on the right side.&lt;/p&gt;
&lt;h2&gt;List Modifiers&lt;/h2&gt;
&lt;p&gt;SwiftUI provides several modifiers that you can apply to lists to customize their behavior and appearance. Here are some commonly used modifiers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/liststyle(_:)"&gt;&lt;code&gt;.listStyle(_:)&lt;/code&gt;&lt;/a&gt;: Changes the style of the list (e.g., &lt;code&gt;.plain&lt;/code&gt;, &lt;code&gt;.grouped&lt;/code&gt;, &lt;code&gt;.insetGrouped&lt;/code&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/navigationtitle(_:)"&gt;&lt;code&gt;.navigationTitle(_:)&lt;/code&gt;&lt;/a&gt;: Sets the title of the navigation bar when the list is embedded in a &lt;code&gt;NavigationView&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/scrollindicators(_:axes:)"&gt;&lt;code&gt;.scrollIndicators(_:)&lt;/code&gt;&lt;/a&gt;: Controls the visibility of scroll indicators in the list.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/dynamicviewcontent/ondelete(perform:)"&gt;&lt;code&gt;.onDelete(perform:)&lt;/code&gt;&lt;/a&gt;: Enables swipe-to-delete functionality for list items.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/dynamicviewcontent/onmove(perform:)"&gt;&lt;code&gt;.onMove(perform:)&lt;/code&gt;&lt;/a&gt;: Enables drag-and-drop reordering of list items.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here's an example of using some of these modifiers:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ContentView: View {
    @State private var items = ["Apple", "Banana", "Cherry", "Date", "Elderberry"]

    var body: some View {
        NavigationView {
            List {
                ForEach(items, id: \.self) { item in
                    Text(item)
                }
                .onDelete(perform: deleteItems)
                .onMove(perform: moveItems)
            }
            .navigationTitle("Fruits")
            .navigationBarItems(
                leading: EditButton(),
                trailing: Button(action: addItem) {
                    Image(systemName: "plus")
                }
            )
        }
    }

    private func deleteItems(at offsets: IndexSet) {
        items.remove(atOffsets: offsets)
    }

    private func moveItems(from source: IndexSet, to destination: Int) {
        items.move(fromOffsets: source, toOffset: destination)
    }

    private func addItem() {
        items.append("New Fruit \(items.count + 1)")
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This example demonstrates how to enable swipe-to-delete and drag-and-drop reordering of list items, as well as adding a button to add new items to the list.&lt;/p&gt;
&lt;h2&gt;Bonus: Indexed lists (SwiftUI)&lt;/h2&gt;
&lt;p&gt;In the code below, we will create an &lt;code&gt;IndexedList&lt;/code&gt; view that displays a list of items grouped by their initial letter. This allows for quick navigation to sections of the list by tapping on the initial letters displayed on the right side of the view.&lt;/p&gt;
&lt;p&gt;I've added a custom implementation of &lt;code&gt;IndexedList&lt;/code&gt; that allows you to create a list with indexed letters on the right side, similar to the Contacts app on iOS. This implementation uses SwiftUI's &lt;a href="https://developer.apple.com/documentation/swiftui/list"&gt;&lt;code&gt;List&lt;/code&gt;&lt;/a&gt; and &lt;code&gt;ScrollViewReader&lt;/code&gt; to create a scrollable list with sections.&lt;/p&gt;
&lt;p&gt;There are other ways to implement this, for example using a hosted &lt;code&gt;UICollectionView&lt;/code&gt; or &lt;code&gt;UITableView&lt;/code&gt;, but this implementation is purely in SwiftUI and leverages the power of SwiftUI's declarative syntax.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;//
//  IndexedList.swift
//  SwiftExtras
//
//  Created by Wesley de Groot on 2025-07-22.
//  https://wesleydegroot.nl
//
//  https://github.com/0xWDG/SwiftExtras
//  MIT License
//

// This works in SwiftUI for both macOS and iOS, allowing you to create a list with indexed letters on the right side.
#if canImport(SwiftUI) &amp;amp;&amp;amp; (os(macOS) || os(iOS))
import SwiftUI

/// IndexedList is a view that displays a list of items grouped by their initial letter.
/// It allows for quick navigation to sections of the list \
/// by tapping on the initial letters displayed on the right side of the view.
/// The list is scrollable and each item can be customized using a cell builder closure.
public struct IndexedList&amp;lt;Cell: View&amp;gt;: View {
    /// The data to be displayed in the list, grouped by initial letter.
    private let listData: [String: [String]]

    /// A closure that builds the view for each cell in the list.
    /// It takes a string (the item name) and returns a view of type `Cell
    private let cellBuilder: (String) -&amp;gt; Cell

    /// Initializes an IndexedList with a list of strings.
    /// The strings are grouped by their initial letter, \
    /// and each item is displayed using the provided cell builder closure.
    /// - Parameters:
    ///   - data: An array of strings to be displayed in the list.
    ///   - rowContent: A closure that takes a string and returns a \
    ///     view of type `Cell` to be used as the content for each row in the list.
    /// - Example:
    /// ```swift
    /// IndexedList(data: ["Apple", "Banana", "Cherry"]) { name in
    ///     Text(name)
    /// }
    /// ```
    public init(
        data: [String],
        @ViewBuilder rowContent: @escaping (String) -&amp;gt; Cell
    ) {
        // Grouping the data by the first letter of each string, converting it to uppercase
        // This creates a dictionary where the keys are the first letters and the values are arrays of strings
        // This allows for quick access to items based on their initial letter
        // The keys are sorted alphabetically to maintain a consistent order in the list
        listData = Dictionary(grouping: data) { name in
            String(name.prefix(1)).uppercased()
        }

        // Assigning the provided rowContent closure to the cellBuilder property
        // This closure will be used to build the view for each cell in the list
        self.cellBuilder = rowContent
    }

    /// The current index that is being hovered over or tapped.
    @State private var currentIndex: String?

    /// A list of index key frames used for determining the position of each indexed letter.
    /// This is used to handle hover and drag gestures for scrolling.
    @State private var indexKeyFrames: [IndexKeyInfo] = []

    /// The body of the IndexedList view.
    public var body: some View {
        // Sorting the keys of the listData dictionary to maintain a consistent order in the list
        // This ensures that the sections are displayed in alphabetical order based on the initial letter
        let sortedKeys = Array(listData.keys).sorted()

        // Using ScrollViewReader to allow programmatic scrolling to sections
        ScrollViewReader { proxy in
            // The main ZStack contains the background color, the List, and the indexed letter column
            ZStack(alignment: .topTrailing) {
                // MARK: - Background Color
                // Dynamic background color based on the platform
                bgColor

                // MARK: - List
                // List displaying the items grouped by their initial letter
                List {
                    // Using ForEach to iterate over the sorted keys of the listData dictionary
                    ForEach(sortedKeys, id: \.self) { key in
                        // Unwrapping the items for the current key
                        if let items = listData[key] {
                            // For each key, we create a section with the header as the key    
                            Section(header: Text(key).id(key)) {
                                // Using ForEach to iterate over the items in each section
                                ForEach(items, id: \.self) { name in
                                    // Building the cell using the provided cell builder closure
                                    cellBuilder(name)
                                }
                            }
                        }
                    }
                }
                // Add extra padding to the list to avoid clipping the indexed letter column
                .padding(.trailing, 10)
                // Hide the scroll indicators for a cleaner look
                .scrollIndicators(.hidden)

                // MARK: - Indexed Letter Column
                // Using a GeometryReader to create a vertical column of indexed letters
                // This column allows users to quickly navigate to sections of the list
                GeometryReader { geo in
                    // A vertical stack containing the indexed letters
                    VStack(spacing: 8) {
                        // Using ForEach to iterate over the sorted keys
                        // Each key is displayed as a tappable text view
                        ForEach(sortedKeys, id: \.self) { key in
                            // Displaying the key as a tappable text view
                            Text(key)
                                // Add a small font
                                .font(.caption2)
                                // Add accent color
                                .foregroundStyle(Color.accentColor)
                                // Add some padding
                                .padding(2)
                                // On tap gesture to scroll to the section corresponding to the key
                                .onTapGesture {
                                    proxy.scrollTo(key, anchor: .top)
                                }
                                // Zoom when hovering over the indexed letter
                                .scaleEffect(currentIndex == key ? 2.0 : 1.0)
                                // Adding a background to add the GeometryReader
                                .background(
                                    // Using GeometryReader to capture the frame of each index key
                                    // For enabling scrubbing on iOS
                                    GeometryReader { proxy in
                                        Color.clear.preference(
                                            key: IndexKeyPreferenceKey.self,
                                            value: [
                                                IndexKeyInfo(
                                                    key: key,
                                                    frame: proxy.frame(in: .global)
                                                )
                                            ]
                                        )
                                    }
                                )
                                // Accessibility traits to make it clear that this is a link
                                .accessibilityAddTraits(.isLink)
                                // Adding hover effect for macOS
                                .onHover { hovering in
                                    if hovering {
                                        currentIndex = key
                                        proxy.scrollTo(key, anchor: .top)
                                    }
                                }
                        }
                    }
                    // Add some trailing padding to the indexed letter column, \
                    // to avoid the text being too close to the edge
                    .padding(.trailing, 8)
                    // Make the height 100% of the available space, \
                    // so it will be centered vertically
                    .frame(maxHeight: .infinity)
                    // Add a drag gesture to allow scrolling through the index
                    // This is particularly useful for iOS where scrubbing is common
                    // The drag gesture updates the current index based on the location of the drag
                    // and scrolls the list to the corresponding section
                    .gesture(
                        // Drag gesture to allow scrolling through the index
                        // For enabling scrubbing on iOS
                        DragGesture(minimumDistance: 1)
                            .onChanged { value in
                                // Check if the current index is nil or if it has changed
                                if let match = indexForLocation(
                                    value.location,
                                    in: geo.frame(in: .global),
                                    keyFrames: indexKeyFrames
                                ) {
                                    // If the current index is nil or has changed, update it and scroll to the section
                                    if currentIndex != match {
                                        currentIndex = match
                                        proxy.scrollTo(match, anchor: .top)
                                    }
                                }
                            }
                            .onEnded { _ in
                                // Reset the current index when the gesture ends
                                currentIndex = nil
                            }
                    )
                }
                // Add a preference key to capture the frames of the indexed letters
                .onPreferenceChange(IndexKeyPreferenceKey.self) { values in
                    self.indexKeyFrames = values
                }
                // Set the frame width of the indexed letter column
                // This ensures that the column has a fixed width for consistency
                // and to avoid layout issues
                .frame(width: 24)
            }
        }
    }

    /// A background color for the IndexedList view.
    /// This color adapts to the platform.
    /// - Returns: A `Color` that represents the background color of the IndexedList view.
    private var bgColor: some View {
#if os(macOS)
        Color(NSColor.windowBackgroundColor)
#else
        Color(UIColor.systemGroupedBackground)
#endif
    }

    /// Determines the index key for a given location in the indexed letter column.
    /// This function checks if the location falls within the frame of any indexed letter.
    /// - Parameters:
    ///   - location: The CGPoint representing the location of the gesture.
    ///   - container: The CGRect representing the frame of the indexed letter column.
    ///   - keyFrames: An array of `IndexKeyInfo` containing the keys and their corresponding frames.
    /// - Returns: The key of the indexed letter that corresponds to the location, or `nil` if no match is found.
    private func indexForLocation(
        _ location: CGPoint,
        in container: CGRect,
        keyFrames: [IndexKeyInfo]
    ) -&amp;gt; String? {
        // Check if the location is within the bounds of the indexed letter column
        for info in keyFrames where info.frame.contains(CGPoint(x: container.midX, y: location.y)) {
            return info.key
        }

        return nil
    }
}

// MARK: - IndexKeyInfo and PreferenceKey
/// A struct that holds information about an indexed letter key and its frame.
/// This is used to capture the frames of the indexed letters for scrubbing functionality.
/// It conforms to `Equatable` to allow comparison between instances.
struct IndexKeyInfo: Equatable {
    let key: String
    let frame: CGRect
}

/// A preference key that collects `IndexKeyInfo` instances.
/// This is used to store the frames of the indexed letters in the IndexedList view.
/// It conforms to `PreferenceKey` to allow the IndexedList view to update its state based \
/// on the frames of the indexed letters.
struct IndexKeyPreferenceKey: PreferenceKey {
    static var defaultValue: [IndexKeyInfo] = []
    static func reduce(value: inout [IndexKeyInfo], nextValue: () -&amp;gt; [IndexKeyInfo]) {
        value.append(contentsOf: nextValue())
    }
}

// MARK: - Preview
// To Preview the IndexedList in SwiftUI previews, you can use the following code snippet:
#Preview {
    IndexedList(data: [
        "Wesley", "Carlo", "Uneata", "Anne", "Bram", "Sanne", "Daan", "Lieke", "Milan",
        "Femke", "Joris", "Tess", "Thijs", "Noa", "Sem", "Lars", "Lotte", "Max", "Eva",
        "Luuk", "Nina", "Finn", "Roos", "Janneke", "Gijs", "Isa", "Koen", "Sofie", "Tijn",
        "Maud", "Ruben", "Evi", "Siem", "Luca", "Bo", "Jelle", "Fleur", "Mees", "Yara",
        "Pim", "Elin", "Stijn", "Mare", "Noud", "Saar", "Tim", "Bente", "Jochem", "Ilse",
        "Pepijn", "Marit", "Teun", "Milou", "Jip", "Jet", "Bas", "Anouk", "Timo", "Veerle",
        "Floris", "Lana", "Jens", "Mila", "Job", "Loes", "Cas", "Tirza", "Nick", "Fenna",
        "Hidde", "Lynn", "Dirk", "Jade", "Mark", "Iris", "Bart", "Elise", "Wout", "Norah",
        "Maarten", "Nora", "Kees", "Tessa", "Rik", "Amber", "Nathan", "Vera", "Roel", "Zara",
        "Jan", "Esmee", "Tom", "Britt", "Stef", "Demi", "Arjen", "Floor", "Johan", "Liv",
        "Harm", "Romy", "Martijn", "Suze", "Kees"
    ]) { name in
        Text(name)
    }
}
#endif&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;You can easily create lists in SwiftUI using the &lt;a href="https://developer.apple.com/documentation/swiftui/list"&gt;&lt;code&gt;List&lt;/code&gt;&lt;/a&gt; view. With its built-in styles and customization options, you can present data in a visually appealing and interactive way. Whether you're displaying simple text or complex custom views, &lt;a href="https://developer.apple.com/documentation/swiftui/list"&gt;&lt;code&gt;List&lt;/code&gt;&lt;/a&gt; provides a powerful tool for building user interfaces in SwiftUI.&lt;/p&gt;</description>
  </item>
  <item>
    <title>Interpolation and formatting in Text</title>    <link>https://wesleydegroot.nl/blog/interpolation-and-formatting-in-text</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/interpolation-and-formatting-in-text</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:30 +0200</pubDate>
    <category>SwiftUI</category>
    <category>Interpolation</category>
    <category>Formatting</category>
    <description>&lt;p&gt;In this post, we will explore how to use interpolation and formatting in SwiftUI's &lt;code&gt;Text&lt;/code&gt; view. This is a powerful feature that allows you to create dynamic and formatted text easily.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;Interpolation and Formatting&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;Interpolation and formatting in SwiftUI's &lt;code&gt;Text&lt;/code&gt; view refer to the ability to insert dynamic values into text strings and apply various formatting options. This allows developers to create rich, user-friendly text displays that can change based on user input or other data.&lt;/p&gt;
&lt;h2&gt;Interpolation in SwiftUI&lt;/h2&gt;
&lt;p&gt;Interpolation in SwiftUI allows you to embed variables and expressions directly within a string. You can use string interpolation by wrapping your variables in parentheses and prefixing them with a backslash. Here's an example:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ContentView: View {
    let name = "Wesley"
    let age = 35

    var body: some View {
        Text("Hello, my name is \(name) and I am \(age) years old.")
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, the &lt;code&gt;Text&lt;/code&gt; view displays a greeting message that includes the &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;age&lt;/code&gt; variables.&lt;/p&gt;
&lt;h2&gt;Formatting in SwiftUI&lt;/h2&gt;
&lt;p&gt;Formatting in SwiftUI allows you to control how values are displayed, such as arrays.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ContentView: View {
    var body: some View {
        let pokemon = ["Dragonite", "Lugia", "Pikachu"]

        Text("Favorite Pokemon: \(pokemon, format: .list(type: .and))")
        // Favorite Pokemon: Dragonite, Lugia and Pikachu
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, the &lt;code&gt;Text&lt;/code&gt; view formats the &lt;code&gt;pokemon&lt;/code&gt; array into a list format, automatically handling the conjunction for the last item.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ContentView: View {
    var body: some View {
        let pokemon = ["Dragonite", "Lugia", "Pikachu"]

        Text("Favorite Pokemon: \(pokemon, format: .list(type: .and))")
            .environment(\.locale, Locale(identifier: "nl"))
        // Favorite Pokemon: Dragonite, Lugia en Pikachu
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Formatting Measurements&lt;/h2&gt;
&lt;p&gt;You can also format measurements in SwiftUI. For example, if you have a measurement in meters and want to display it in kilometers, you can do the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ContentView: View {
    var body: some View {
        let distance = Measurement(value: 1000, unit: UnitLength.meters)

        // You walked 3,281 mi.
        Text("You walked \(distance, format: .measurement(width: .abbreviated)).")
            .environment(\.locale, Locale(identifier: "en_US"))

        // You walked 1 km.
        Text("You walked \(distance, format: .measurement(width: .abbreviated)).")
            .environment(\.locale, Locale(identifier: "nl"))
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;p&gt;While interpolation and formatting in SwiftUI's &lt;code&gt;Text&lt;/code&gt; view are powerful, there are some caveats to keep in mind:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Performance&lt;/strong&gt;: Excessive use of interpolation and formatting can lead to performance issues, especially in complex views. Use them judiciously.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Localization&lt;/strong&gt;: When using interpolation and formatting, ensure that your strings are localized properly to support different languages and regions. SwiftUI provides tools for localization, but you need to manage your strings carefully.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;In this post, we explored how to use interpolation and formatting in SwiftUI's &lt;code&gt;Text&lt;/code&gt; view. By leveraging these features, you can create dynamic and user-friendly text displays that adapt to your app's needs.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/text"&gt;https://developer.apple.com/documentation/swiftui/text&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Placing components within the Safe Area Inset</title>    <link>https://wesleydegroot.nl/blog/placing-components-within-the-safe-area-inset</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/placing-components-within-the-safe-area-inset</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:31 +0200</pubDate>
    <category>SwiftUI</category>
    <category>SafeArea</category>
    <description>&lt;p&gt;In this post, we will explore how to effectively place components within the Safe Area Inset in SwiftUI applications.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;Safe Area Inset&lt;/code&gt; and &lt;a href="https://developer.apple.com/documentation/swiftui/view/safeareainset(edge:alignment:spacing:content:)-6gwby"&gt;&lt;code&gt;.safeAreaInset&lt;/code&gt;&lt;/a&gt; modifier?&lt;/h2&gt;
&lt;p&gt;The Safe Area Inset is a layout guide in iOS that defines the portion of the screen where content can be safely displayed without being obscured by system UI elements like the status bar, navigation bar, and home indicator. In SwiftUI, the Safe Area Inset is automatically taken into account when laying out views, but there are times when you may want to customize the placement of your components within this area.&lt;br&gt;
The &lt;a href="https://developer.apple.com/documentation/swiftui/view/safeareainset(edge:alignment:spacing:content:)-6gwby"&gt;&lt;code&gt;safeAreaInset(edge:alignment:spacing:content:)&lt;/code&gt;&lt;/a&gt; modifier allows you to specify additional padding for your views, ensuring they remain within the safe area.&lt;/p&gt;
&lt;h2&gt;Use Case: Display an important view at all times&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct SafeAreaInset: View {
    var body: some View {
        ZStack {
            LinearGradient(colors: [.black, .blue], startPoint: .top, endPoint: .bottom)
                .ignoresSafeArea()

            VStack(spacing: 20) {
                Text("Safe Area Insets Example")
                    .font(.title)
                    .fontWeight(.bold)
                    .foregroundColor(.white)
            }
        }
        .safeAreaInset(edge: .bottom) {
            Text("I am inset from the bottom safe area")
                .foregroundStyle(.white)
        }
    }
}

#Preview {
    SafeAreaInset()
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/resources/SafeAreaInset.png" alt="Safe Area Insets Example" loading="lazy"&gt;&lt;/p&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;p&gt;When using Safe Area Insets, it's important to be aware of the following caveats:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Dynamic Content&lt;/strong&gt;: If your content changes dynamically (e.g., due to user input or network responses), you may need to adjust your layout accordingly to ensure it remains within the safe area.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Device Variability&lt;/strong&gt;: Different devices have different safe area insets (e.g., iPhones with and without a notch). Always test your layout on multiple devices to ensure a consistent experience.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Overlapping Views&lt;/strong&gt;: Be cautious when layering views. If you have overlapping views, make sure they respect the safe area insets to avoid being obscured by system UI elements.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;Safe area insets are a crucial aspect of designing user interfaces in iOS applications. By understanding and utilizing safe area insets, you can ensure that your content is displayed correctly and is not obscured by system UI elements.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/safeareainset(edge:alignment:spacing:content:)-6gwby"&gt;https://developer.apple.com/documentation/swiftui/view/safeareainset(edge:alignment:spacing:content:)-6gwby&lt;/a&gt;&lt;/p&gt;</description>
  </item>
  <item>
    <title>Building Editable Lists in SwiftUI</title>    <link>https://wesleydegroot.nl/blog/building-editable-lists-in-swiftui</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/building-editable-lists-in-swiftui</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:32 +0200</pubDate>
    <category>SwiftUI</category>
    <category>List</category>
    <description>&lt;p&gt;In this post, we will explore how to create editable lists in SwiftUI using a couple of different approaches using &lt;code&gt;EditButton()&lt;/code&gt;, &lt;code&gt;@Environment(\.editMode)&lt;/code&gt; and custom &lt;code&gt;EditMode&lt;/code&gt; state.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;List&lt;/code&gt;, &lt;code&gt;EditButton()&lt;/code&gt;, &lt;code&gt;EditMode&lt;/code&gt; and &lt;code&gt;@Environment(\.editMode)&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/list"&gt;&lt;code&gt;List&lt;/code&gt;&lt;/a&gt; is a SwiftUI view that presents a scrollable list of rows. It is commonly used to display collections of data in a structured way.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://developer.apple.com/documentation/swiftui/editbutton"&gt;&lt;code&gt;EditButton()&lt;/code&gt;&lt;/a&gt; is a built-in SwiftUI button that toggles the edit mode of a view. When tapped, it switches the view between editing and non-editing states, please note this will only update the &lt;a href="https://developer.apple.com/documentation/swiftui/list"&gt;&lt;code&gt;List&lt;/code&gt;&lt;/a&gt;'s edit mode, it cannot be read from somewhere else in the view.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://developer.apple.com/documentation/swiftui/editmode"&gt;&lt;code&gt;EditMode&lt;/code&gt;&lt;/a&gt; is an enumeration that represents the different states of a view's edit mode. It has two cases we want to use &lt;a href="https://developer.apple.com/documentation/swiftui/editmode/inactive"&gt;&lt;code&gt;.inactive&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://developer.apple.com/documentation/swiftui/editmode/active"&gt;&lt;code&gt;.active&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://developer.apple.com/documentation/swiftui/environmentvalues/editmode"&gt;&lt;code&gt;@Environment(\.editMode)&lt;/code&gt;&lt;/a&gt; property wrapper allows you to access the current edit mode of the view, enabling you to create editable lists.&lt;/p&gt;
&lt;h2&gt;Example &lt;code&gt;List&lt;/code&gt; with &lt;code&gt;EditButton()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;To create an editable list, you can use the &lt;a href="https://developer.apple.com/documentation/swiftui/list"&gt;&lt;code&gt;List&lt;/code&gt;&lt;/a&gt; view in combination with &lt;a href="https://developer.apple.com/documentation/swiftui/editbutton"&gt;&lt;code&gt;EditButton()&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct Pokemon: Identifiable {
    let id: UUID
    let name: String

    init(name: String) {
        self.id = UUID()
        self.name = name
    }
}

struct EditModeView: View {
    @State private var pokemon: [Pokemon] = [
        .init(name: "Dragonite"),
        .init(name: "Lugia"),
        .init(name: "Pikachu"),
        .init(name: "Mewtwo")
    ]

    @State private var selectedIems: Set&amp;lt;UUID&amp;gt; = []

    var body: some View {
        NavigationStack {
            List(selection: $selectedIems) {
                ForEach(pokemon) { pokemon in
                    Text(pokemon.name)
                }
                .onDelete { pokemon.remove(atOffsets: $0) }
                .onMove { pokemon.move(fromOffsets: $0, toOffset: $1) }
            }
            .toolbar {
                ToolbarItem(placement: .primaryAction) {
                    EditButton()
                }
            }
        }
    }
}

#Preview {
    EditModeView()
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Example &lt;code&gt;List&lt;/code&gt; and &lt;code&gt;@Environment(\.editMode)&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;To create an editable &lt;a href="https://developer.apple.com/documentation/swiftui/list"&gt;&lt;code&gt;List&lt;/code&gt;&lt;/a&gt; using &lt;a href="https://developer.apple.com/documentation/swiftui/environmentvalues/editmode"&gt;&lt;code&gt;@Environment(\.editMode)&lt;/code&gt;&lt;/a&gt;, you can follow this example:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct EditModeView: View {
    @Environment(\.editMode) private var editMode
    @State private var name = "Firstname Lastname"

    var body: some View {
        NavigationStack {
            Form {
                if editMode?.wrappedValue.isEditing == true {
                    TextField("Name", text: $name)
                } else {
                    Text(name)
                }
            }
            .animation(nil, value: editMode?.wrappedValue)
            .toolbar {
                EditButton()
            }
        }
    }
}

#Preview {
    EditModeView()
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Example &lt;code&gt;List&lt;/code&gt; with custom &lt;code&gt;EditMode&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;To create an editable &lt;a href="https://developer.apple.com/documentation/swiftui/list"&gt;&lt;code&gt;List&lt;/code&gt;&lt;/a&gt; using a custom &lt;a href="https://developer.apple.com/documentation/swiftui/editmode"&gt;&lt;code&gt;EditMode&lt;/code&gt;&lt;/a&gt; state, you can follow this example:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct Pokemon: Identifiable {
    let id: UUID
    let name: String

    init(name: String) {
        self.id = UUID()
        self.name = name
    }
}

struct EditModeView: View {
    // Custom edit mode.
    @State var editMode: EditMode = .inactive

    @State private var pokemon: [Pokemon] = [
        .init(name: "Dragonite"),
        .init(name: "Lugia"),
        .init(name: "Pikachu"),
        .init(name: "Mewtwo")
    ]

    @State private var selectedIems: Set&amp;lt;UUID&amp;gt; = []

    var body: some View {
        NavigationStack {
            List(selection: $selectedIems) {
                ForEach(pokemon) { pokemon in
                    Text(pokemon.name)
                }
                .onDelete { pokemon.remove(atOffsets: $0) }
                .onMove { pokemon.move(fromOffsets: $0, toOffset: $1) }
            }
            // Tell the List that we are if editting
            .environment(\.editMode, $editMode)
            .toolbar {
                ToolbarItem(placement: .primaryAction) {
                    // Toggle editmode
                    Button(editMode.isEditing ? "Done" : "Edit") {
                        withAnimation {
                            editMode = editMode.isEditing ? .inactive : .active
                        }
                    }
                }

                ToolbarItem(placement: .bottomBar) {
                    VStack {
                        Text("Selected: \(selectedIems.count), edit mode: \(editMode.isEditing)")

                        HStack {
                            if selectedIems.isEmpty {
                                Button("Select All") {
                                    selectedIems.formUnion(pokemon.map(\.id))
                                    editMode = .active
                                }
                            } else {
                                Button("Deselect All") {
                                    selectedIems.removeAll()
                                    editMode = .inactive
                                }
                            }

                            Spacer()

                            Button("Delete") {
                                pokemon.removeAll(where: {
                                    selectedIems.contains($0.id)
                                })
                                selectedIems = []
                                editMode = .inactive
                            }
                            .disabled(selectedIems.isEmpty)
                            .disabled(!editMode.isEditing)
                            .frame(maxWidth: .infinity)
                        }
                    }
                    .frame(maxWidth: .infinity)
                }
            }
        }
    }
}

#Preview {
    EditModeView()
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/environmentvalues/editmode"&gt;&lt;code&gt;@Environment(\.editMode)&lt;/code&gt;&lt;/a&gt; only passes the &lt;code&gt;EditMode&lt;/code&gt; to the &lt;a href="https://developer.apple.com/documentation/swiftui/list"&gt;&lt;code&gt;List&lt;/code&gt;&lt;/a&gt;/&lt;code&gt;Form&lt;/code&gt; and will not be updated anywhere else, that's why you need to manage the edit mode state manually if you want to support "Select All", "Deselect All", and "Delete" as a button.&lt;/p&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;In this article, we explored how to create editable lists in SwiftUI using a custom edit mode. We covered the basics of managing selection and deletion of items, as well as how to toggle edit mode. With these techniques, you can create more interactive and user-friendly list interfaces in your SwiftUI applications.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/list"&gt;https://developer.apple.com/documentation/swiftui/list&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/editbutton"&gt;https://developer.apple.com/documentation/swiftui/editbutton&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/editmode"&gt;https://developer.apple.com/documentation/swiftui/editmode&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/environmentvalues/editmode"&gt;https://developer.apple.com/documentation/swiftui/environmentvalues/editmode&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Building SwiftUI Debugging Utilities</title>    <link>https://wesleydegroot.nl/blog/building-swiftui-debugging-utilities</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/building-swiftui-debugging-utilities</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:32 +0200</pubDate>
    <category>SwiftUI</category>
    <category>Debug</category>
    <description>&lt;p&gt;In this post, we will explore how to create debugging utilities in SwiftUI to help diagnose and fix issues in your app.&lt;/p&gt;
&lt;h2&gt;Printing values&lt;/h2&gt;
&lt;p&gt;As you may know already you cannot use &lt;a href="https://developer.apple.com/documentation/swift/print(_:separator:terminator:)"&gt;&lt;code&gt;print()&lt;/code&gt;&lt;/a&gt; directly in your SwiftUI code.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct ContentView: View {
    var body: some View {
        Text("Hello, World!")
        let _ = print("This is a debug message") // Print in the supported way
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A way to achieve this functionality is creating a extension on View.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;extension View {
    func print(_ value: Any) -&amp;gt; Self {
        Swift.print(value) // Swift.print to tell Swift that we want to use the native print function
        return self
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;now we can use:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct ContentView: View {
    var body: some View {
        Text("Hello, World!")
            .print("This is a debug message")
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Perform custom debugging actions&lt;/h2&gt;
&lt;p&gt;Sometimes you want to execute code only on the debug build.&lt;br&gt;
One way is:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct ContentView: View {
    var body: some View {
        Text("Hello, World!")
#if DEBUG
            .print("This is a debug message")
#endif
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A better way to achieve this functionality is creating a extension on View.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;extension View {
    func debugAction(_ closure: () -&amp;gt; Void) -&amp;gt; Self {
        #if DEBUG
        closure()
        #endif

        return self
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;now we can use:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct ContentView: View {
    var body: some View {
        Text("Hello, World!")
            .debugAction {
                print("This is a debug message")
            }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Debugging view borders&lt;/h2&gt;
&lt;p&gt;Sometimes you want to see where the views are located on the screen. You can achieve this by adding borders to your views.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;extension View {
    func debugBorder(_ color: Color = .red) -&amp;gt; some View {
#if DEBUG
        self.border(color, width: 2)
#else
        self
#endif
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can use:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct ContentView: View {
    var body: some View {
        Text("Hello, World!")
            .debugBorder()
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Writing a pair of functions to print debug messages and add borders to views can greatly enhance the debugging experience in SwiftUI. By using conditional compilation and view extensions, we can create a clean and efficient debugging workflow.&lt;/p&gt;</description>
  </item>
  <item>
    <title>Remove the background from images using Swift</title>    <link>https://wesleydegroot.nl/blog/remove-the-background-from-images-using-swift</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/remove-the-background-from-images-using-swift</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:33 +0200</pubDate>
    <category>Swift</category>
    <description>&lt;p&gt;Removing backgrounds from images is a common task in modern apps, whether for photo editing, social media, or automation. In this article, you'll learn how to build a cross-platform background remover in Swift using Apple's Vision and Core Image frameworks. We'll walk through the concepts, code, and practical tips to help you integrate this feature into your iOS or macOS projects.&lt;/p&gt;
&lt;h2&gt;Technologies Used&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Swift&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Core Image&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Vision Framework&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;UIKit / AppKit&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Why Background Removal?&lt;/h2&gt;
&lt;p&gt;Background removal lets you isolate subjects, create transparent images, and enhance user experiences. Apple's Vision framework makes this possible with powerful machine learning tools, while Core Image handles image processing. We'll use both to create a reusable Swift class.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This implementation is cross-platform (iOS &amp;amp; macOS) For a ready-to-use version use &lt;a href="https://github.com/0xWDG/SwiftExtras"&gt;SwiftExtras&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Step 1: Cross-Platform Setup&lt;/h2&gt;
&lt;p&gt;We'll create a &lt;code&gt;BackgroundRemover&lt;/code&gt; class that works on both iOS and macOS. To handle platform differences, we use a &lt;a href="https://docs.swift.org/swift-book/documentation/the-swift-programming-language/declarations/#:~:text=the%20following%20form:-,typealias,-%3C#name#%3E"&gt;&lt;code&gt;typealias&lt;/code&gt;&lt;/a&gt; for the image type: &lt;code&gt;NSImage&lt;/code&gt; for macOS and &lt;code&gt;UIImage&lt;/code&gt; for iOS.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;@available(iOS 17.0, macOS 14.0, *)
class BackgroundRemover {
#if os(macOS)
    // Support macOS
    typealias PlatformNativeImage = NSImage
#else
    // Support iOS
    typealias PlatformNativeImage = UIImage
#endif
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 2: Error Handling&lt;/h2&gt;
&lt;p&gt;To make our code robust, we define custom errors for common failure cases. This helps with debugging and makes the API safer to use.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;enum Errors: Error {
    case failedToUnwrapImage
    case failedToCreateCIImage
    case failedToRenderCGImage
    case failedToApplyMask
    case failedToCreateMask
    case invalidImageData
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 3: Generate the Foreground Mask&lt;/h2&gt;
&lt;p&gt;The Vision framework can detect the subject in an image and create a mask. This mask is used to separate the foreground from the background.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key APIs:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/vision/vngenerateforegroundinstancemaskrequest"&gt;&lt;code&gt;VNGenerateForegroundInstanceMaskRequest()&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/vision/vnimagerequesthandler"&gt;&lt;code&gt;VNImageRequestHandler(ciImage:)&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/coreimage/ciimage/init(cvpixelbuffer:)"&gt;&lt;code&gt;CIImage(cvPixelBuffer:)&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Note:&lt;/strong&gt; This code does not run on the iOS simulator due to Vision framework limitations. Use a real device or macOS.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;private func createMask(from inputImage: CIImage) -&amp;gt; CIImage? {
    // Create a request to generate the foreground instance mask
    let request = VNGenerateForegroundInstanceMaskRequest()
    // Create a handler to perform the request
    let handler = VNImageRequestHandler(ciImage: inputImage)
    do {
        // Perform the request
        try handler.perform([request])
        // Get the first result
        if let result = request.results?.first {
            // Generate the mask from the result
            let mask = try result.generateScaledMaskForImage(
                forInstances: result.allInstances,
                from: handler
            )

            // Return the mask as a CIImage
            return CIImage(cvPixelBuffer: mask)
        }
    } catch {
        // Something went wrong
        print(error)
    }

    // Something went wrong
    return nil
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 4: Apply the Mask to Remove the Background&lt;/h2&gt;
&lt;p&gt;With the mask generated, we use Core Image to blend the mask with the original image, effectively removing the background.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key APIs:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/coreimage/cifilter/3228274-blendwithmask"&gt;&lt;code&gt;CIFilter.blendWithMask()&lt;/code&gt;&lt;/a&gt; (requires &lt;code&gt;import CoreImage.CIFilterBuiltins&lt;/code&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;private func applyMask(mask: CIImage, to image: CIImage) -&amp;gt; CIImage? {
    // Create a blend filter
    let filter = CIFilter.blendWithMask()
    // Set the input image
    filter.inputImage = image
    // Set the mask image
    filter.maskImage = mask
    // Set the background image to be empty
    filter.backgroundImage = CIImage.empty()
    // Return the output image
    return filter.outputImage
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 5: Render the Final Image&lt;/h2&gt;
&lt;p&gt;After applying the mask, we render the result to a platform-native image type (&lt;code&gt;UIImage&lt;/code&gt; or &lt;code&gt;NSImage&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key APIs:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/coreimage/cicontext/createcgimage(_:from:)"&gt;&lt;code&gt;CIContext.createCGImage(_:from:)&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uiimage/init(cgimage:)"&gt;&lt;code&gt;UIImage(cgImage:)&lt;/code&gt;&lt;/a&gt; (iOS)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/appkit/nsimage/init(cgimage:size:)"&gt;&lt;code&gt;NSImage(cgImage:size:)&lt;/code&gt;&lt;/a&gt; (macOS)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;private func removeBackground(from inputImage: CIImage) throws -&amp;gt; PlatformNativeImage {
    // Create a CIContext to render the output image
    let context = CIContext(options: nil)

    // Create a mask image, see createMask(from:)
    guard let maskImage = createMask(from: inputImage) else {
        throw Errors.failedToCreateMask
    }
    // Apply the mask to the input image, see applyMask(mask:to:)
    guard let outputImage = applyMask(mask: maskImage, to: inputImage) else {
        throw Errors.failedToApplyMask
    }

    // Render the output image
    guard let cgImage = context.createCGImage(outputImage, from: outputImage.extent) else {
        throw Errors.failedToRenderCGImage
    }

    #if os(iOS)
    return PlatformNativeImage(cgImage: cgImage)
    #else
    return PlatformNativeImage(cgImage: cgImage, size: CGSize(width: cgImage.width, height: cgImage.height))
    #endif
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 6: Parse and Remove the Background&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;parse(image:)&lt;/code&gt; method ties everything together. It converts the input image to a &lt;code&gt;CIImage&lt;/code&gt;, processes it, and returns the result with the background removed.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;/// Parses the image and removes the background.
///
/// - Parameter image: The image to parse.
/// - Returns: The image with the background removed.
public func parse(image: PlatformNativeImage) throws -&amp;gt; PlatformNativeImage {
#if os(iOS)
    guard let ciImage = CIImage(image: image) else {
        throw Errors.failedToCreateCIImage
    }
#else
    guard let cgImage = image.cgImage(forProposedRect: nil, context: nil, hints: nil) else {
        throw Errors.failedToCreateCIImage
    }
    let ciImage = CIImage(cgImage: cgImage)
#endif

    return try removeBackground(from: ciImage)
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Complete Implementation&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;#if canImport(Vision) &amp;amp;&amp;amp; canImport(CoreImage.CIFilterBuiltins)
import Vision
import CoreImage.CIFilterBuiltins
#if canImport(UIKit)
import UIKit
#endif
#if canImport(AppKit)
import AppKit
#endif

@available(iOS 17.0, macOS 14.0, *)
class BackgroundRemover {
    enum Errors: Error {
        /// Failed to unwrap the image.
        case failedToUnwrapImage

        /// Failed to create a CIImage from the input image.
        case failedToCreateCIImage

        /// Failed to render the CGImage.
        case failedToRenderCGImage

        /// Failed to apply the mask to the image.
        case failedToApplyMask

        /// Failed to create the mask.
        case failedToCreateMask

        /// Invalid image data.
        case invalidImageData
    }

#if os(macOS)
    /// Platform-specific image type. (macOS)
    typealias PlatformNativeImage = NSImage
#else
    /// Platform-specific image type. (iOS)
    typealias PlatformNativeImage = UIImage
#endif

    /// Removes the background from the image.
    ///
    /// - Parameter inputImage: The image from which to remove the background.
    /// - Returns: The image with the background removed.
    private func removeBackground(from inputImage: CIImage) throws -&amp;gt; PlatformNativeImage {
        // Create a CIContext to render the output image
        let context = CIContext(options: nil)

        // Create a mask image, see createMask(from:)
        guard let maskImage = createMask(from: inputImage) else {
            throw Errors.failedToCreateMask
        }
        // Apply the mask to the input image, see applyMask(mask:to:)
        guard let outputImage = applyMask(mask: maskImage, to: inputImage) else {
            throw Errors.failedToApplyMask
        }

        // Render the output image
        guard let cgImage = context.createCGImage(outputImage, from: outputImage.extent) else {
            // Failed to render CGImage
            throw Errors.failedToRenderCGImage
        }

        // Create the final output image
#if os(iOS)
        return PlatformNativeImage(cgImage: cgImage)
#else
        return PlatformNativeImage(cgImage: cgImage, size: CGSize(width: cgImage.width, height: cgImage.height))
#endif
    }

    /// Creates a mask from the input image.
    ///
    /// - Parameter inputImage: The image from which to create the mask.
    /// - Returns: The mask image, or nil if creation failed.
    private func createMask(from inputImage: CIImage) -&amp;gt; CIImage? {
        // Create a request to generate the foreground instance mask
        let request = VNGenerateForegroundInstanceMaskRequest()
        // Create a handler for the image request
        let handler = VNImageRequestHandler(ciImage: inputImage)

        do {
            // Perform the request
            try handler.perform([request])
            // Get the first result
            if let result = request.results?.first {
                // Generate the mask for the image
                let mask = try result.generateScaledMaskForImage(
                    forInstances: result.allInstances,
                    from: handler
                )

                // Create the final mask image
                return CIImage(cvPixelBuffer: mask)
            }
        } catch {
            // Failed to generate mask
            print(error)
        }

        // Failed to generate mask
        return nil
    }

    /// Applies the mask to the image.
    ///
    /// - Parameters:
    ///   - mask: The mask image to apply.
    ///   - image: The image to which the mask will be applied.
    /// - Returns: The image with the mask applied, or nil if the operation failed.
    private func applyMask(mask: CIImage, to image: CIImage) -&amp;gt; CIImage? {
        // Create a blend filter
        let filter = CIFilter.blendWithMask()
        // Set the input image
        filter.inputImage = image
        // Set the mask image
        filter.maskImage = mask
        // Set the background image to be empty
        filter.backgroundImage = CIImage.empty()
        // Return the output image
        return filter.outputImage
    }

    /// Removes the background from the image.
    ///
    /// - Parameter image: The image from which to remove the background.
    /// - Returns: The image with the background removed.
    public func parse(image: PlatformNativeImage) throws -&amp;gt; PlatformNativeImage {
#if os(iOS)
        // iOS: Create a CIImage from the UIImage
        guard let ciImage = CIImage(image: image) else {
            throw Errors.failedToCreateCIImage
        }
#else
        // macOS: Create a CIImage from the NSImage
        guard let cgImage = image.cgImage(forProposedRect: nil, context: nil, hints: nil) else {
            throw Errors.failedToCreateCIImage
        }
        let ciImage = CIImage(cgImage: cgImage)
#endif

        // Remove the background from the image
        return try removeBackground(from: ciImage)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For a production-ready version and more utilities, see &lt;a href="https://github.com/0xWDG/SwiftExtras/blob/main/Sources/SwiftExtras/Classes/BackgroundRemover.swift"&gt;SwiftExtras on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Wrap Up&lt;/h2&gt;
&lt;p&gt;With just a few lines of Swift, you can leverage Apple's frameworks to perform advanced image background removal on both iOS and macOS. This approach is efficient, easy to integrate, and can be extended for more complex use cases.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;You now have a solid foundation for implementing background removal in Swift. Experiment with the code, adapt it to your needs, and explore the resources below for further reading and inspiration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Further Resources:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://tiagohenriques.vercel.app/blog/image-background-remover-cli"&gt;Image Background Remover CLI&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://www.artemnovichkov.com/blog/remove-background-from-image-in-swiftui"&gt;Remove Background from Image in SwiftUI&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Custom Tabbar with SwiftUI</title>    <link>https://wesleydegroot.nl/blog/custom-tabbar-with-swiftui</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/custom-tabbar-with-swiftui</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:33 +0200</pubDate>
    <category>SwiftUI</category>
    <category>TabBar</category>
    <description>&lt;p&gt;In this post, we will explore how to create a custom tab bar using SwiftUI.&lt;/p&gt;
&lt;h2&gt;What is a &lt;code&gt;TabView&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;A &lt;a href="https://developer.apple.com/documentation/swiftui/tabview"&gt;&lt;code&gt;TabView&lt;/code&gt;&lt;/a&gt; is a user interface component that allows users to switch between different views or sections of an app. Unlike the default tab bar provided by SwiftUI, a custom tab bar can be tailored to fit the specific design and functionality needs of your application.&lt;/p&gt;
&lt;h2&gt;How to Build a Custom TabBar in SwiftUI&lt;/h2&gt;
&lt;p&gt;SwiftUI’s built-in &lt;a href="https://developer.apple.com/documentation/swiftui/tabview"&gt;&lt;code&gt;TabView&lt;/code&gt;&lt;/a&gt; is great for quick setups, but sometimes you want more control over the look and feel of your app’s navigation. Whether you're aiming for a sleek, animated tab bar or something that matches your brand’s aesthetic, building a custom tab bar in SwiftUI is surprisingly approachable.&lt;/p&gt;
&lt;h2&gt;Step 1: Define Your Tab Items&lt;/h2&gt;
&lt;p&gt;We'll start by creating a model to represent each tab:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;enum TabItem: String, CaseIterable {
    case home = "house"
    case search = "magnifyingglass"
    case profile = "person"

    var title: String {
        switch self {
        case .home: return "Home"
        case .search: return "Search"
        case .profile: return "Profile"
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 2: Create the Custom Tab Bar View&lt;/h2&gt;
&lt;p&gt;Now let’s build the tab bar UI:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct CustomTabBar: View {
    // This will update our selected tab
    @Binding var selectedTab: TabItem

    var body: some View {
        HStack {
            // Walk through each tab item
            ForEach(TabItem.allCases, id: \.self) { tab in
                // Add a spacer (before the item)
                Spacer()
                VStack {
                    // Tab icon
                    Image(systemName: tab.rawValue)
                        .font(.system(size: 20, weight: .bold))

                    // Tab title
                    Text(tab.title)
                        .font(.caption)
                }
                // By placing the foregroundColor modifier here, the color gets changed in all components in our VStack.
                .foregroundColor(selectedTab == tab ? .blue : .gray)
                // On tap, we update the selected tab with animation.
                .onTapGesture {
                    withAnimation {
                        selectedTab = tab
                    }
                }
                // This makes the entire tab area accessible
                .accessibilityAddTraits(.isButton)
                // Add a spacer (after the item)
                Spacer()
            }
        }
        .padding()
        // Set the background color and shadow
        .background(
            Color(.systemBackground)
                .shadow(radius: 2)
        )
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This view uses &lt;code&gt;@Binding&lt;/code&gt; so it can update the selected tab from the parent view.&lt;/p&gt;
&lt;h2&gt;Step 3: Create the Main View&lt;/h2&gt;
&lt;p&gt;Now let’s create the main view that uses the custom tab bar:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct ContentView: View {
    // This will save our selected tab
    @State private var selectedTab: TabItem = .home

    var body: some View {
        VStack {
            switch selectedTab {
            case .home:
                Text("Home") // Replace with your Home view
            case .search:
                Text("Search") // Replace with your Search view
            case .profile:
                Text("Profile") // Replace with your Profile view
            }
        }
        // We'll set the max width and height to fill the available space
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        // We'll place our custom tab bar in the safe area.
        .safeAreaInset(edge: .bottom) {
            CustomTabBar(selectedTab: $selectedTab)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each tab displays a different view, and the tab bar sits at the bottom.&lt;/p&gt;
&lt;h2&gt;Screenshot&lt;/h2&gt;
&lt;p&gt;&lt;img src="/resources/CustomTabbar.png" alt="Custom TabBar" loading="lazy"&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Building a custom tab bar in SwiftUI is a straightforward process that allows for a high degree of customization, it is a great way to enhance the user experience in your app.&lt;/p&gt;
&lt;p&gt;Resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/tabview"&gt;https://developer.apple.com/documentation/swiftui/tabview&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Hidden macOS features</title>    <link>https://wesleydegroot.nl/blog/hidden-macos-features</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/hidden-macos-features</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:34 +0200</pubDate>
    <category>macOS</category>
    <description>&lt;p&gt;In this post we're going to explore some hidden features of macOS that can enhance your productivity and improve your workflow.&lt;/p&gt;
&lt;h3&gt;Move a file.&lt;/h3&gt;
&lt;p&gt;To move a file in finder you can use &lt;code&gt;⌘C&lt;/code&gt; (Command + C) to copy the file,&lt;br&gt;
then navigate to the destination folder and use command &lt;code&gt;⌘⌥V&lt;/code&gt; (Command + Option + V) to move the file instead of copying it.&lt;/p&gt;
&lt;h3&gt;Hidden menu options.&lt;/h3&gt;
&lt;p&gt;A lot of menu options are hidden, you can reveal them by holding down the &lt;code&gt;Option&lt;/code&gt; key while clicking on the menu. This will often show additional options that are not visible by default.&lt;/p&gt;
&lt;h3&gt;Resizing windows.&lt;/h3&gt;
&lt;p&gt;You can quickly resize windows by holding down the &lt;code&gt;Option&lt;/code&gt; key while dragging the edges or corners. This will allow you to resize the window from the center, rather than from the edge.&lt;/p&gt;
&lt;h3&gt;Apple Logo&lt;/h3&gt;
&lt;p&gt;You can type &lt;code&gt;Option + Shift + K&lt;/code&gt; to quickly insert the Apple logo () in your documents.&lt;/p&gt;
&lt;h3&gt;Screenshot a window&lt;/h3&gt;
&lt;p&gt;Press &lt;code&gt;Command + Shift + 4&lt;/code&gt; to enter screenshot mode, then press the &lt;code&gt;Space&lt;/code&gt; bar to switch to window mode. Click on the window you want to capture.&lt;/p&gt;
&lt;h3&gt;Screenshot to clipboard&lt;/h3&gt;
&lt;p&gt;Holding &lt;code&gt;control&lt;/code&gt; while taking a screenshot immediately saves it to the clipboard.&lt;/p&gt;
&lt;h3&gt;Screen Recording&lt;/h3&gt;
&lt;p&gt;Press &lt;code&gt;Command + Shift + 5&lt;/code&gt; to enter screen recording mode. You can choose to record the entire screen or a selected portion.&lt;/p&gt;
&lt;h3&gt;Open Settings&lt;/h3&gt;
&lt;p&gt;Press &lt;code&gt;Command + ,&lt;/code&gt; (Command + Comma) to quickly open the settings of the currently open application.&lt;/p&gt;
&lt;h3&gt;Move cursor quickly in the terminal.&lt;/h3&gt;
&lt;p&gt;Option click in terminal jumps your cursor to that position in the terminal.&lt;/p&gt;
&lt;h3&gt;Quick Look&lt;/h3&gt;
&lt;p&gt;Press the &lt;code&gt;Space&lt;/code&gt; bar while a file is selected in Finder to quickly preview the file without opening it.&lt;/p&gt;
&lt;h3&gt;No state restoration&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Shift&lt;/code&gt; + Click on an app in the Dock will reset its state before opening it.&lt;/p&gt;
&lt;h3&gt;Cycle between windows of the current application.&lt;/h3&gt;
&lt;p&gt;Press &lt;code&gt;Command + `&lt;/code&gt; (Command + Backtick) to quickly cycle between windows of the current application.&lt;br&gt;
(On some keyboards, try the &lt;code&gt;~&lt;/code&gt; (tilde) key instead)&lt;/p&gt;
&lt;h3&gt;Hide current application&lt;/h3&gt;
&lt;p&gt;Press &lt;code&gt;Command + H&lt;/code&gt; (Command + H) to quickly hide the currently active application.&lt;/p&gt;
&lt;h3&gt;(Un)hide the dock&lt;/h3&gt;
&lt;p&gt;Press &lt;code&gt;Command + Option + D&lt;/code&gt; (Command + Option + D) to quickly hide or show the dock.&lt;/p&gt;</description>
  </item>
  <item>
    <title>Sensory feedback in SwiftUI</title>    <link>https://wesleydegroot.nl/blog/sensory-feedback-in-swiftui</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/sensory-feedback-in-swiftui</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:34 +0200</pubDate>
    <category>SwiftUI</category>
    <category>Trigger</category>
    <description>&lt;p&gt;This post explores the concept of sensory feedback in SwiftUI, a modifier to provide feedback to users based on their interactions.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;Sensory Feedback in SwiftUI&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Sensory Feedback in SwiftUI&lt;/code&gt; is a powerful feature that allows developers to create responsive and interactive user interfaces. It enables the triggering of actions and updates in the UI based on user interactions or changes in data.&lt;/p&gt;
&lt;p&gt;Sensory feedback can include haptic feedback, sound effects, and visual cues that enhance the user experience by providing immediate and relevant responses to user actions.&lt;/p&gt;
&lt;h2&gt;What are the options&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/sensoryfeedback(_:trigger:)"&gt;&lt;code&gt;.sensoryfeedback(_:trigger:)&lt;/code&gt;&lt;/a&gt; allows you to specify the type of feedback and the trigger for that feedback.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/sensoryfeedback/success"&gt;&lt;code&gt;.success&lt;/code&gt;&lt;/a&gt;: Indicates that a task or action has completed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/sensoryfeedback/warning"&gt;&lt;code&gt;.warning&lt;/code&gt;&lt;/a&gt;: Indicates that a task or action has produced a warning of some kind.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/sensoryfeedback/error"&gt;&lt;code&gt;.error&lt;/code&gt;&lt;/a&gt;: Indicates that an error has occurred.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Use Case: Button Press Feedback&lt;/h2&gt;
&lt;p&gt;In this example, we provide haptic feedback when a button is pressed:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ContentView: View {
    @State private var action = true
    var body: some View {
        Button("Tap me") {
            action.toggle()
        }
        .sensoryFeedback(.success, trigger: action)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Use Case: Button Press Feedback (Custom intensity)&lt;/h2&gt;
&lt;p&gt;In this example, we provide haptic feedback with custom intensity when a button is pressed:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct ContentView: View {
    @State private var trigger = false

    var body: some View {
        NavigationStack {
            Button("Action") {
                // do something
                trigger.toggle()
            }
            .sensoryFeedback(
                .impact(weight: .heavy, intensity: 0.9),
                trigger: trigger
            )
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Use Case Button Press Feedback (based on value)&lt;/h2&gt;
&lt;p&gt;In this example, we provide haptic feedback based on the value of a state variable when a button is pressed:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ContentView: View {
    @State private var action = true
    var body: some View {
        Button("Tap me") {
            action.toggle()
        }
        .sensoryFeedback(trigger: action) { oldValue, newValue in
            return newValue.isEmpty ? .error : .success
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;p&gt;While &lt;code&gt;Sensory Feedback in SwiftUI&lt;/code&gt; can greatly enhance user interactions, it's important to use it judiciously. Overusing haptic feedback or other sensory cues can lead to a cluttered user experience. Always consider the context and the user's needs when implementing these features.&lt;/p&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;In summary, &lt;code&gt;Sensory Feedback in SwiftUI&lt;/code&gt; is a valuable tool for creating responsive and interactive user interfaces. By providing immediate feedback based on user interactions, it can significantly enhance the overall user experience.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In conclusion, &lt;code&gt;Sensory Feedback in SwiftUI&lt;/code&gt; is an essential aspect of modern app development. By incorporating haptic feedback, sound effects, and visual cues, developers can create more engaging and intuitive user experiences. As you explore SwiftUI, consider how you can leverage these features to improve your app's interactivity and responsiveness.&lt;/p&gt;
&lt;p&gt;Resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/sensoryfeedback"&gt;https://developer.apple.com/documentation/swiftui/sensoryfeedback&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/sensoryfeedback(_:trigger"&gt;https://developer.apple.com/documentation/swiftui/view/sensoryfeedback(_:trigger&lt;/a&gt;:)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Hacktoberfest 2025</title>    <link>https://wesleydegroot.nl/blog/hacktoberfest-2025</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/hacktoberfest-2025</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:35 +0200</pubDate>
    <category>Hacktoberfest</category>
    <description>&lt;p&gt;As the leaves change color and the air turns crisp, developers around the world gear up for one of the most exciting events in the open-source community: &lt;strong&gt;Hacktoberfest&lt;/strong&gt;.&lt;br&gt;
This annual celebration, organized by DigitalOcean, GitHub, and other partners, encourages developers to contribute to open-source projects throughout October.&lt;br&gt;
If you're a (Swift) developer, Hacktoberfest is the perfect opportunity to dive into the world of open source, enhance your skills, and make meaningful contributions.&lt;br&gt;
Let's explore how you can make the most of Hacktoberfest as a (Swift) developer.&lt;/p&gt;
&lt;h4&gt;What is Hacktoberfest?&lt;/h4&gt;
&lt;p&gt;Hacktoberfest is a month-long event that promotes open-source contributions.&lt;br&gt;
Participants are encouraged to submit pull requests (PRs) to open-source repositories on GitHub.&lt;br&gt;
By completing a certain number of PRs, participants can earn rewards.&lt;/p&gt;
&lt;h4&gt;Why Should Swift Developers Participate?&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Skill Enhancement&lt;/strong&gt;: Contributing to open-source projects allows you to work on real-world problems, enhancing your coding skills and learning new techniques.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Community Engagement&lt;/strong&gt;: Hacktoberfest is a great way to connect with other developers, share knowledge, and collaborate on exciting projects.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Portfolio Building&lt;/strong&gt;: Contributions to open-source projects are a valuable addition to your portfolio, showcasing your skills to potential employers.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Giving Back&lt;/strong&gt;: Open source thrives on community contributions. By participating, you help improve the tools and libraries that you and others use daily.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;Getting Started with Hacktoberfest&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Sign Up&lt;/strong&gt;: Register for Hacktoberfest on the &lt;a href="https://hacktoberfest.com/"&gt;official website&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Find Projects&lt;/strong&gt;: Look for Swift projects that need contributions. You can filter repositories by language on GitHub to find Swift-specific projects.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Read the Guidelines&lt;/strong&gt;: Each project has its own contribution guidelines. Make sure to read and follow them to ensure your PRs are accepted.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Start Contributing&lt;/strong&gt;: Begin by tackling issues labeled "Hacktoberfest" or "good first issue." These are typically beginner-friendly and a great way to get started.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;Tips for Successful Contributions&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Communicate&lt;/strong&gt;: Engage with project maintainers and other contributors. Ask questions if you're unsure about something.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Write Clear PRs&lt;/strong&gt;: Ensure your pull requests are well-documented and clearly explain the changes you've made.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Test Thoroughly&lt;/strong&gt;: Make sure your code works as expected and doesn't introduce new bugs.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Be Respectful&lt;/strong&gt;: Open-source communities thrive on respect and collaboration. Be courteous and constructive in your interactions.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Recommended Swift Projects for Hacktoberfest&lt;/h4&gt;
&lt;p&gt;Here are a few Swift projects that welcome contributions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/AuroraEditor/AuroraEditor"&gt;AuroraEditor&lt;/a&gt;: Aurora Editor is a IDE built by the community, for the community, and written in Swift for the best native performance and feel for macOS.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/realm/SwiftLint"&gt;SwiftLint&lt;/a&gt;: A tool to enforce Swift style and conventions.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/vapor/vapor"&gt;Vapor&lt;/a&gt;: A web framework for Swift.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;All of &lt;a href="https://github.com/stars/0xWDG/lists/my-spm-packages"&gt;my SPM projects&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Search on Hacktoberfest on &lt;a href="https://github.com/topics/hacktoberfest"&gt;GitHub&lt;/a&gt;, &lt;a href="https://gitlab.com/explore/projects/topics/hacktoberfest"&gt;GitLab&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;Hacktoberfest is a fantastic opportunity for Swift developers to contribute to the open-source community, learn new skills, and connect with like-minded individuals. Whether you're a seasoned developer or just starting out, there's a place for you in Hacktoberfest. So, roll up your sleeves, dive into some code, and make this October a month of meaningful contributions.&lt;/p&gt;
&lt;p&gt;For more information and to register, visit the &lt;a href="https://hacktoberfest.com/"&gt;Hacktoberfest website&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy Hacktoberfest!&lt;/p&gt;</description>
  </item>
  <item>
    <title>ViewThatFits</title>    <link>https://wesleydegroot.nl/blog/viewthatfits</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/viewthatfits</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:36 +0200</pubDate>
    <category>SwiftUI</category>
    <category>ViewThatFits</category>
    <description>&lt;p&gt;SwiftUI is all about declarative UI and adaptive design. But sometimes, you need your view to intelligently choose between multiple layout options depending on the available space. That's where &lt;code&gt;ViewThatFits&lt;/code&gt; comes in a sleek, space-aware container introduced in iOS 16 that makes responsive design a breeze.&lt;/p&gt;
&lt;p&gt;Let's explore how it works and why it's a game-changer for building flexible interfaces.&lt;/p&gt;
&lt;h2&gt;What Is &lt;code&gt;ViewThatFits&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/viewthatfits"&gt;&lt;code&gt;ViewThatFits&lt;/code&gt;&lt;/a&gt; is a layout container that evaluates its child views in order and displays the first one that fits within the available space. Think of it as a smart switchboard—it tries each view until it finds one that works.&lt;/p&gt;
&lt;h3&gt;Syntax Overview:&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;ViewThatFits {
    Text("This is a long version of the text that might not fit.")
    Text("Shorter version.")
    Text("Tiny.")
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, SwiftUI will try to render the first &lt;code&gt;Text&lt;/code&gt;. If it doesn't fit, it moves to the second, and so on.&lt;/p&gt;
&lt;h2&gt;Real-World Use Case: Responsive Buttons&lt;/h2&gt;
&lt;p&gt;Let's say you want a button that shows a full label on larger screens and a compact icon on smaller ones.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;ViewThatFits {
    Label("Download", systemImage: "arrow.down.circle")
        .padding()
        .background(Color.blue)
        .foregroundColor(.white)
        .clipShape(Capsule())

    Image(systemName: "arrow.down.circle")
        .padding()
        .background(Color.blue)
        .foregroundColor(.white)
        .clipShape(Circle())
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This layout automatically adapts to the space available—no need for manual size checks or geometry readers.&lt;/p&gt;
&lt;h2&gt;Adaptive Toolbars&lt;/h2&gt;
&lt;p&gt;Toolbars are another great use case. You might want to show a full set of controls on iPad, but simplify them on iPhone.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;ViewThatFits(in: .horizontal) {
    HStack {
        Button("Edit") { }
        Button("Share") { }
        Button("Delete") { }
    }

    HStack {
        Image(systemName: "pencil")
        Image(systemName: "square.and.arrow.up")
        Image(systemName: "trash")
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;.horizontal&lt;/code&gt; parameter tells SwiftUI to evaluate fitting based on horizontal space only.&lt;/p&gt;
&lt;h2&gt;Tips and Gotchas&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Order matters&lt;/strong&gt;: SwiftUI picks the &lt;em&gt;first&lt;/em&gt; view that fits, so arrange from most desirable to most compact.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Performance&lt;/strong&gt;: Keep child views lightweight—SwiftUI evaluates each one until it finds a match.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Combine with &lt;code&gt;LayoutPriority&lt;/code&gt;&lt;/strong&gt;: For even finer control, you can use layout priorities inside each child view.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Why It Matters&lt;/h2&gt;
&lt;p&gt;Before &lt;a href="https://developer.apple.com/documentation/swiftui/viewthatfits"&gt;&lt;code&gt;ViewThatFits&lt;/code&gt;&lt;/a&gt;, developers had to rely on &lt;a href="https://developer.apple.com/documentation/swiftui/geometryreader"&gt;&lt;code&gt;GeometryReader&lt;/code&gt;&lt;/a&gt;, conditional logic, or custom layout algorithms to achieve responsive behavior. Now, it's declarative, elegant, and built right into SwiftUI.&lt;/p&gt;
&lt;p&gt;Whether you're building for iPhone SE or a 12.9" iPad Pro, &lt;a href="https://developer.apple.com/documentation/swiftui/viewthatfits"&gt;&lt;code&gt;ViewThatFits&lt;/code&gt;&lt;/a&gt; helps your UI gracefully adapt without breaking a sweat.&lt;/p&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;SwiftUI continues to evolve, and &lt;a href="https://developer.apple.com/documentation/swiftui/viewthatfits"&gt;&lt;code&gt;ViewThatFits&lt;/code&gt;&lt;/a&gt; is a perfect example of its philosophy: write less code, get more adaptability. It's a small but mighty tool that can make your layouts smarter and your code cleaner.&lt;/p&gt;
&lt;p&gt;Ready to give it a try? Drop it into your next layout and watch your UI respond like magic.&lt;/p&gt;
&lt;p&gt;Resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/viewthatfits"&gt;https://developer.apple.com/documentation/swiftui/viewthatfits&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/geometryreader"&gt;https://developer.apple.com/documentation/swiftui/geometryreader&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Swift Package: ImagePicker</title>    <link>https://wesleydegroot.nl/blog/swift-package-imagepicker</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/swift-package-imagepicker</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:37 +0200</pubDate>
    <category>SwiftPM</category>
    <category>ImagePicker</category>
    <description>&lt;p&gt;In this post, we will explore the &lt;code&gt;ImagePicker&lt;/code&gt; Swift Package, a powerful tool for selecting images in your applications.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;ImagePicker&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;ImagePicker&lt;/code&gt; is a Swift Package that simplifies the process of selecting images from the user's photo library or taking new photos with the camera. It provides a customizable and easy-to-use interface for integrating image picking functionality into your SwiftUI applications.&lt;/p&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;p&gt;To install the &lt;code&gt;ImagePicker&lt;/code&gt; package, you can use the Swift Package Manager. Add the following line to your &lt;code&gt;Package.swift&lt;/code&gt; file:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;.package(url: "https://github.com/0xWDG/ImagePicker", branch: "main")&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, add &lt;code&gt;ImagePicker&lt;/code&gt; to the dependencies of your target:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;.target(
    name: "YourApp",
    dependencies: ["ImagePicker"]
)&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;How to use &lt;code&gt;ImagePicker&lt;/code&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI
import ImagePicker

struct ContentView: View {
    @State
    private var image: Image?

    var body: some View {
        VStack {
            ImagePicker(image: $image)
                .frame(width: 150, height: 150)
        }
        .padding()
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Make sure to handle permissions for accessing the photo library.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;ImagePicker&lt;/code&gt; Swift Package is a powerful tool for integrating image selection into your SwiftUI applications. Its customizable interface and ease of use make it a great choice for developers looking to enhance their apps with image picking functionality.&lt;/p&gt;
&lt;p&gt;Resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/0xWDG/ImagePicker"&gt;https://github.com/0xWDG/ImagePicker&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui"&gt;https://developer.apple.com/documentation/swiftui&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Contextmenu</title>    <link>https://wesleydegroot.nl/blog/contextmenu</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/contextmenu</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:38 +0200</pubDate>
    <category>SwiftUI</category>
    <category>Contextmenu</category>
    <description>&lt;p&gt;SwiftUI continues to surprise us with its elegant solutions to common UI patterns. One such gem is the &lt;a href="https://developer.apple.com/documentation/swiftui/view/contextmenu(_:)"&gt;&lt;code&gt;ContextMenu&lt;/code&gt;&lt;/a&gt;—a powerful modifier that lets you attach contextual actions to any view. Think of it as the right-click menu for touch interfaces.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;Contextmenu&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://developer.apple.com/documentation/swiftui/view/contextmenu(_:)"&gt;&lt;code&gt;Contextmenu&lt;/code&gt;&lt;/a&gt; in SwiftUI is a view modifier that allows you to present a context menu when the user performs a secondary click (right-click) on a view. This is particularly useful for providing additional options or actions related to the content the user is interacting with.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ContentView: View {
    var body: some View {
        Text("Long press me")
            .padding()
            .contextMenu {
                Button(action: {
                    print("Edit tapped")
                }) {
                    Label("Edit", systemImage: "pencil")
                }

                Button(action: {
                    print("Delete tapped")
                }) {
                    Label("Delete", systemImage: "trash")
                }
            }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Use Cases&lt;/h2&gt;
&lt;p&gt;Context menus are useful in scenarios like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;File management (rename, delete, duplicate)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;List items with secondary actions&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Image previews with quick edits&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Admin dashboards with hidden controls&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;They’re especially useful when you want to keep your UI clean but still offer power-user features.&lt;/p&gt;
&lt;h2&gt;Platform Behavior&lt;/h2&gt;
&lt;p&gt;ContextMenus behave differently depending on the platform:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;Trigger&lt;/th&gt;
&lt;th&gt;Appearance&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;iOS&lt;/td&gt;
&lt;td&gt;Long press&lt;/td&gt;
&lt;td&gt;Floating menu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;td&gt;Right-click&lt;/td&gt;
&lt;td&gt;Native context menu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;watchOS&lt;/td&gt;
&lt;td&gt;Not supported&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;On iOS, they feel like a native part of the system. On macOS, they integrate seamlessly with right-click behavior. Just be mindful of platform limitations.&lt;/p&gt;
&lt;h2&gt;Customizing the Experience&lt;/h2&gt;
&lt;p&gt;Conditionally show menu items:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;.contextMenu {
    Button("Share", action: {
        print("Share tapped")
    }) {
        Label("Share", systemImage: "square.and.arrow.up")
    }
    if isAdmin {
        Button("Ban User", action: {
            print("Ban User tapped")
        }) {
            Label("Ban User", systemImage: "person.crop.square")
        }
    }
    Button("Report", action: {
        print("Report tapped")
    }) {
        Label("Report", systemImage: "exclamationmark.bubble")
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This keeps your menus dynamic and relevant.&lt;/p&gt;
&lt;h2&gt;Best Practices&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Keep context menus short and focused.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use system icons for familiarity.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Avoid placing critical actions (like "Delete") without confirmation.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Test on all platforms.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;SwiftUI’s &lt;a href="https://developer.apple.com/documentation/swiftui/view/contextmenu(_:)"&gt;&lt;code&gt;ContextMenu&lt;/code&gt;&lt;/a&gt; is a subtle but powerful tool. It helps you declutter your UI while still offering rich interactions. Whether you're building a productivity app or a social platform, context menus can elevate your user experience.&lt;/p&gt;
&lt;p&gt;Want to see more SwiftUI tricks? Check out my post on &lt;a href="https://wesleydegroot.nl/blog/Popovers-in-SwiftUI"&gt;Popovers in SwiftUI&lt;/a&gt; for another clean way to present options without navigating away.&lt;/p&gt;</description>
  </item>
  <item>
    <title>Swift Package: FilePicker</title>    <link>https://wesleydegroot.nl/blog/swift-package-filepicker</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/swift-package-filepicker</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:38 +0200</pubDate>
    <category>SwiftPM</category>
    <category>FilePicker</category>
    <description>&lt;p&gt;&lt;a href="https://github.com/0xWDG/FilePicker"&gt;FilePicker&lt;/a&gt; is a Swift Package to open and save files in SwiftUI. It provides a simple way to open and save files in SwiftUI views. &lt;/p&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;p&gt;To install FilePicker, add it to your &lt;code&gt;Package.swift&lt;/code&gt; file:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;dependencies: [
    .package(url: "https://github.com/0xWDG/FilePicker", branch: "main")
]&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Use Case: Open a file&lt;/h2&gt;
&lt;p&gt;Opening a file:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI
import FilePicker

struct ContentView: View {
    // MARK: Filepicker
    @State var filePickerOpen = false
    @State var filePickerFiles: [URL] = []

    var body: some View {
        VStack {
            Text("Open a file :)")
                .padding()

            Button("Open", systemImage: "square.and.arrow.down") {
                filePickerOpen.toggle()
            }

            ForEach(filePickerFiles, id: \.self) { file in
                Text(file.lastPathComponent)
            }
        }
        .padding()
        .filePicker(
            isPresented: $filePickerOpen,
            files: $filePickerFiles,
            types: [.json, .text], // Optional (default: .json)
            allowsMultipleSelection: false // Optional (default: false)
        )
        .onChange(of: $filePickerFiles.wrappedValue) { newValue in
            print(newValue)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Use Case: Save a file&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI
import FilePicker

struct ContentView: View {
    // MARK: Filepicker
    @State var filePickerOpen = false
    var filePickerFileName = "test.txt"
    var filePickerData = Data("Hello, World!".utf8)

    var body: some View {
        VStack {
            Text("Save a file :)")
                .padding()

            Button("Save", systemImage: "square.and.arrow.up") {
                filePickerOpen.toggle()
            }
        }
        .padding()
        .filePicker(
            isPresented: $filePickerOpen,
            fileName: filePickerFileName,
            data: filePickerData,
            types: [.text]
        )
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;FilePicker is a Swift Package to open and save files in SwiftUI. It provides a simple way to open and save files in SwiftUI views.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/0xWDG/FilePicker"&gt;FilePicker&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://swiftpackageindex.com/0xWDG/FilePicker"&gt;Swift Package Index&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Swift Package: iCloudStorage</title>    <link>https://wesleydegroot.nl/blog/swift-package-icloudstorage</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/swift-package-icloudstorage</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:39 +0200</pubDate>
    <category>SwiftPM</category>
    <category>iCloudStorage</category>
    <description>&lt;p&gt;In this post, we will explore the &lt;code&gt;iCloudStorage&lt;/code&gt; Swift Package, a powerful tool for managing iCloud key-value storage in your applications.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;iCloudStorage&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;iCloudStorage&lt;/code&gt; is a Swift Package that simplifies the process of storing and retrieving key-value pairs in iCloud. It provides a straightforward API for interacting with iCloud's key-value storage, making it easy to persist user preferences and app state across devices.&lt;/p&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;p&gt;To install the &lt;code&gt;iCloudStorage&lt;/code&gt; package, you can use the Swift Package Manager. Add the following line to your &lt;code&gt;Package.swift&lt;/code&gt; file:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;.package(url: "https://github.com/0xWDG/iCloudStorage", branch: "main")&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, add &lt;code&gt;iCloudStorage&lt;/code&gt; to the dependencies of your target:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;.target(
    name: "YourApp",
    dependencies: ["iCloudStorage"]
)&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Use Case: Storing User Preferences&lt;/h2&gt;
&lt;p&gt;Imagine you want to store user preferences, such as a theme selection or a user ID, across app launches. With &lt;code&gt;iCloudStorage&lt;/code&gt;, you can easily save and retrieve these values.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI
import iCloudStorage

struct ContentView: View {
    @iCloudStorage("key")
    var value: String = "default"

    var body: some View {
        VStack {
            Text(value)

            Button("Change value") {
                value = "Hello there at \(Date())"
            }
        }
        .task {
            value = "Hello there"
        }
        .padding()
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Make sure to handle errors when accessing iCloud storage.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Be aware of iCloud's limitations on data size and types.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;iCloudStorage&lt;/code&gt; Swift Package is a powerful tool for managing iCloud key-value storage in your applications. Its simple API and seamless integration with SwiftUI make it an excellent choice for developers looking to enhance their apps with persistent storage.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/0xWDG/iCloudStorage"&gt;https://github.com/0xWDG/iCloudStorage&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui"&gt;https://developer.apple.com/documentation/swiftui&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Swift Package: SecureStorage</title>    <link>https://wesleydegroot.nl/blog/swift-package-securestorage</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/swift-package-securestorage</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:39 +0200</pubDate>
    <category>SwiftPM</category>
    <category>SecureStorage</category>
    <description>&lt;p&gt;In this post, we will explore the &lt;code&gt;SecureStorage&lt;/code&gt; Swift Package, a powerful tool for managing sensitive data in your applications.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;SecureStorage&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;SecureStorage&lt;/code&gt; is a Swift Package that simplifies the process of storing and retrieving sensitive data, such as passwords and tokens, securely. It provides a straightforward API for interacting with the Keychain, making it easy to persist sensitive information across app launches.&lt;/p&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;p&gt;To install the &lt;code&gt;SecureStorage&lt;/code&gt; package, you can use the Swift Package Manager. Add the following line to your &lt;code&gt;Package.swift&lt;/code&gt; file:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;.package(url: "https://github.com/0xWDG/SecureStorage", branch: "main")&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, add &lt;code&gt;SecureStorage&lt;/code&gt; to the dependencies of your target:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;.target(
    name: "YourApp",
    dependencies: ["SecureStorage"]
)

## Use Case: Storing User Credentials

Imagine you want to securely store user credentials, such as a username and password, in your application. With `SecureStorage`, you can easily save and retrieve these values.

```swift
import SwiftUI
import SecureStorage

struct ContentView: View {
    @SecureStorage("username")
    var username: String = ""

    @SecureStorage("password")
    var password: String = ""

    var body: some View {
        VStack {
            TextField("Username", text: $username)
            SecureField("Password", text: $password)

            Button("Save Credentials") {
                // Save the credentials securely
            }
        }
        .padding()
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Make sure to handle errors when accessing the Keychain.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Be aware of Keychain's limitations on data size and types.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;SecureStorage&lt;/code&gt; Swift Package is a powerful tool for managing sensitive data in your applications. Its simple API and seamless integration with SwiftUI make it an excellent choice for developers looking to enhance their apps with secure storage.&lt;/p&gt;
&lt;p&gt;Resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/0xWDG/SecureStorage"&gt;https://github.com/0xWDG/SecureStorage&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/security/keychain_services"&gt;https://developer.apple.com/documentation/security/keychain_services&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Supporting Reduced Motion accessibility setting in SwiftUI</title>    <link>https://wesleydegroot.nl/blog/supporting-reduced-motion-accessibility-setting-in-swiftui</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/supporting-reduced-motion-accessibility-setting-in-swiftui</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:40 +0200</pubDate>
    <category>SwiftUI</category>
    <category>Accessibility</category>
    <description>&lt;p&gt;In this post, we will explore how to support the Reduced Motion accessibility setting in SwiftUI applications.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;Reduced Motion&lt;/code&gt; accessibility setting in SwiftUI?&lt;/h2&gt;
&lt;p&gt;The Reduced Motion accessibility setting in iOS allows users to minimize the amount of motion and animation in the user interface. This is particularly helpful for individuals who may experience discomfort or distraction from excessive motion.&lt;/p&gt;
&lt;h2&gt;Supporting Reduced Motion accessibility setting in SwiftUI&lt;/h2&gt;
&lt;p&gt;In a SwiftUI application, you can respect the user's preference for reduced motion by using the &lt;code&gt;@Environment&lt;/code&gt; property wrapper to access the &lt;code&gt;colorScheme&lt;/code&gt; and &lt;code&gt;accessibilityReduceMotion&lt;/code&gt; environment values. This allows you to conditionally adjust your animations and transitions based on the user's settings.&lt;/p&gt;
&lt;h2&gt;Get the reduced motion setting&lt;/h2&gt;
&lt;p&gt;To get the current reduced motion setting, you can use the following code snippet:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;@Environment(\.accessibilityReduceMotion) var reduceMotion&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Implementation&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct ContentView: View {
    @Environment(\.accessibilityReduceMotion) private var reduceMotion

    var body: some View {
        Image(systemName: "star")
            .rotationEffect(reduceMotion ? .zero : .degrees(360))
            .animation(
                reduceMotion ? nil : .linear(duration: 1).repeatForever(autoreverses: false),
                value: reduceMotion
            )
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: If we also wanted to check whether Prefer Cross-Fade Transitions is enabled, we can use UIAccessibility.prefersCrossFadeTransitions. There's currently no environment value available for that setting in SwiftUI.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;In summary, supporting the Reduced Motion accessibility setting in SwiftUI is essential for creating a more inclusive user experience. By leveraging the &lt;code&gt;@Environment&lt;/code&gt; property wrapper and being mindful of animation duration and user preferences, you can ensure that your app respects the needs of all users.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;</description>
  </item>
  <item>
    <title>Swift Package: XCUITestHelper</title>    <link>https://wesleydegroot.nl/blog/swift-package-xcuitesthelper</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/swift-package-xcuitesthelper</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:40 +0200</pubDate>
    <category>SwiftPM</category>
    <category>XCUITestHelper</category>
    <description>&lt;p&gt;In this post, we will explore the &lt;code&gt;XCUITestHelper&lt;/code&gt; Swift Package, a powerful tool for simplifying UI testing in your applications.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;XCUITestHelper&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;XCUITestHelper&lt;/code&gt; is a Swift Package that provides a set of utilities and extensions to make writing UI tests easier and more efficient.&lt;/p&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;p&gt;To install &lt;code&gt;XCUITestHelper&lt;/code&gt;, you can add it to your Swift Package dependencies. Here’s how you can do it:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;.package(url: "https://github.com/0xWDG/XCUITestHelper", branch: "main")&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, add &lt;code&gt;XCUITestHelper&lt;/code&gt; to the dependencies of your target:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;.target(
    name: "YourApp",
    dependencies: ["XCUITestHelper"]
)&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Use Case: Simplified UI Testing&lt;/h2&gt;
&lt;p&gt;Imagine you want to write UI tests for your application. With &lt;code&gt;XCUITestHelper&lt;/code&gt;, you can easily streamline your test code and make it more readable.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import XCTest
import XCUITestHelper

final class MyAppUITests: XCTestCase {
    override func setUpWithError() throws {
        continueAfterFailure = false
    }

    func testExample() throws {
        // UI tests must launch the application that they test.
        let app = XCUIApplication()

        // * Set the app language to English.
        app.setLanguage(to: .english)
        // Do this before launching the app.
        app.launch()

        // * `Wait` for 1 second to continue
        app.wait(for: 1)

        // * Tap a `random` cell in a collection view.
        // Random works with any kind of element, not just buttons.
        app.collectionViews.buttons.random.tap()

        // * Go back to previous screen (NavigationView)
        app.navigateBack()

        // * Tap on the last button
        app.buttons.lastMatch.tap()

        // * Tap on the second button
        app.buttons[1].tap()

        // * Type something, and then clear it.
        let textfield = app.searchFields.firstMatch
        app.type(in: textfield, text: "a", action: .clear)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Make sure to handle errors when accessing UI elements.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Be aware of potential race conditions when waiting for elements to appear.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;XCUITestHelper&lt;/code&gt; Swift Package is a powerful tool for simplifying UI testing in your applications. Its utilities and extensions can help you write more readable and maintainable test code.&lt;/p&gt;
&lt;p&gt;Resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/0xWDG/XCUITestHelper"&gt;https://github.com/0xWDG/XCUITestHelper&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/xctest/ui_testing"&gt;https://developer.apple.com/documentation/xctest/ui_testing&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Swift Package: PreventScreenshot</title>    <link>https://wesleydegroot.nl/blog/swift-package-preventscreenshot</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/swift-package-preventscreenshot</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:41 +0200</pubDate>
    <category>SwiftPM</category>
    <category>PreventScreenshot</category>
    <description>&lt;p&gt;In this post, we will explore a Swift package designed to prevent screenshots in iOS applications.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;PreventScreenshot&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;PreventScreenshot&lt;/code&gt; is a Swift package that provides a simple way to prevent users from taking screenshots of sensitive information displayed in your iOS app. This can be particularly useful for applications that handle sensitive data, such as banking or healthcare apps.&lt;/p&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;p&gt;To install &lt;code&gt;PreventScreenshot&lt;/code&gt;, you can use the Swift Package Manager. Add the following line to your &lt;code&gt;Package.swift&lt;/code&gt; file:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;.package(url: "https://github.com/0xWDG/PreventScreenshot", branch: "main")&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, add &lt;code&gt;PreventScreenshot&lt;/code&gt; to the dependencies of your target:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;.target(
    name: "YourApp",
    dependencies: ["PreventScreenshot"]),&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Use Case: Preventing Screenshots of Sensitive Information&lt;/h2&gt;
&lt;p&gt;In a banking app, for example, you may want to prevent users from taking screenshots of their account balance or transaction history. By using &lt;code&gt;PreventScreenshot&lt;/code&gt;, you can easily implement this functionality.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI
import PreventScreenshot

struct ContentView: View {
    var body: some View {
        VStack {
            Text("Don't take a screenshot of this")
                .preventScreenshot()
        }
        .padding()
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;p&gt;While &lt;code&gt;PreventScreenshot&lt;/code&gt; can be an effective way to protect sensitive information, it's important to note that it may not be foolproof. Users may still find ways to capture sensitive information through other means, such as using a camera or a secondary device. Therefore, it's essential to combine this approach with other security measures to ensure the protection of sensitive data.&lt;br&gt;
Using &lt;code&gt;PreventScreenshot&lt;/code&gt; may decrease the overall user experience, as it can be seen as intrusive. It's crucial to find a balance between security and usability when implementing such measures.&lt;/p&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;In conclusion, &lt;code&gt;PreventScreenshot&lt;/code&gt; is a valuable tool for developers looking to enhance the security of their iOS applications. By preventing screenshots of sensitive information, you can help protect your users' data and maintain their trust in your app.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/0xWDG/PreventScreenshot"&gt;https://github.com/0xWDG/PreventScreenshot&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Swift Package: NetworkMonitor</title>    <link>https://wesleydegroot.nl/blog/swift-package-networkmonitor</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/swift-package-networkmonitor</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:41 +0200</pubDate>
    <category>SwiftPM</category>
    <category>NetworkMonitor</category>
    <description>&lt;p&gt;In this post, we will explore a Swift package designed to monitor network connectivity in iOS applications.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;NetworkMonitor&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;NetworkMonitor&lt;/code&gt; is a Swift package that provides a simple way to monitor the network connectivity status of your iOS app. This can be particularly useful for applications that rely on network access to function properly.&lt;/p&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;p&gt;To install &lt;code&gt;NetworkMonitor&lt;/code&gt;, you can use the Swift Package Manager. Add the following line to your &lt;code&gt;Package.swift&lt;/code&gt; file:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;.package(url: "https://github.com/0xWDG/NetworkMonitor", branch: "main")&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, add &lt;code&gt;NetworkMonitor&lt;/code&gt; to the dependencies of your target:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;.target(
    name: "YourApp",
    dependencies: ["NetworkMonitor"]),&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Use Case&lt;/h2&gt;
&lt;p&gt;Sometimes, you may want to perform certain actions based on the network connectivity status. For example, you might want to show a "No Internet Connection" screen when the user is offline or retry a network request when the connection is restored.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI
import NetworkMonitor

struct ContentView: View {
    @ObservedObject
    private var network = NetworkMonitor()

    var body: some View {
        VStack {
            Text("Hello!")
            Text("The network status is \(network.isConnected ? "Connected" : "Disconnected")")
            Text("You are using a \"\(network.isExpensive ? "Expensive" : "Normal")\" internet connection")

            HStack(spacing: 0) {
                Text("You are using \"")
                switch (network.networkType) {
                case .cellular:
                    Text("Celluar")
                case .wifi:
                    Text("Wifi")
                case .loopback:
                    Text("Loopback")
                case .other:
                    Text("Other")
                case .wiredEthernet:
                    Text("Wired")
                default:
                    Text("Unknown")
                }
                Text("\" to connect to the internet")
            }
        }.task {
            print(network.nwPath)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;p&gt;While &lt;code&gt;NetworkMonitor&lt;/code&gt; can be a useful tool for monitoring network connectivity, it's important to keep in mind that it may not cover all edge cases. For example, changes in network status may not be immediately reflected in the UI, and there may be a delay between when the network status changes and when the app is notified of the change. Additionally, the package may not account for all possible network configurations and scenarios.&lt;/p&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;In summary, &lt;code&gt;NetworkMonitor&lt;/code&gt; is a valuable tool for developers looking to enhance the network connectivity monitoring capabilities of their iOS applications. By providing a simple and effective way to track network status changes, it can help improve the overall user experience and ensure that your app behaves as expected in different network conditions.&lt;/p&gt;
&lt;p&gt;Resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/0xWDG/NetworkMonitor"&gt;https://github.com/0xWDG/NetworkMonitor&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>The Identifiable Protocol in Swift</title>    <link>https://wesleydegroot.nl/blog/identifiable-protocol-in-swift</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/identifiable-protocol-in-swift</guid>
    <pubDate>Thu, 22 Jan 2026 17:55:15 +0100</pubDate>
    <category>Swift</category>
    <category>Identifiable</category>
    <category>SwiftUI</category>
    <description>&lt;p&gt;The Identifiable protocol is a fundamental building block in Swift and SwiftUI that enables unique identification of instances. Understanding how to effectively use Identifiable improves code clarity, enables powerful SwiftUI features, and provides type-safe identity management across your applications.&lt;/p&gt;
&lt;h2&gt;Understanding Identifiable&lt;/h2&gt;
&lt;p&gt;The Identifiable protocol requires a single property, &lt;code&gt;id&lt;/code&gt;, that uniquely identifies each instance:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;protocol Identifiable {
    associatedtype ID: Hashable
    var id: ID { get }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This simple requirement enables powerful features in SwiftUI and Swift collections:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct User: Identifiable {
    let id: UUID
    let name: String
    let email: String

    init(name: String, email: String) {
        self.id = UUID()
        self.name = name
        self.email = email
    }
}

// Usage in SwiftUI
struct UsersListView: View {
    let users: [User]

    var body: some View {
        List(users) { user in // No need to specify \.id
            Text(user.name)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Why Use Identifiable?&lt;/h2&gt;
&lt;p&gt;Identifiable provides several key benefits for Swift and SwiftUI development:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;// Without Identifiable - verbose and error-prone
struct Product {
    let sku: String
    let name: String
}

List(products, id: \.sku) { product in // Must specify id manually
    Text(product.name)
}

// With Identifiable - clean and type-safe
struct Product: Identifiable {
    let id: String // Using SKU as identifier
    let name: String
}

List(products) { product in // Automatic identification
    Text(product.name)
}

// Identifiable enables powerful diffing
class ProductStore: ObservableObject {
    @Published var products: [Product] = []

    func updateProduct(_ updated: Product) {
        // Swift can efficiently find and update the correct product
        if let index = products.firstIndex(where: { $0.id == updated.id }) {
            products[index] = updated
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Identifiable with Different ID Types&lt;/h2&gt;
&lt;p&gt;The ID type can be any Hashable type:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;// UUID identifier - best for unique entities
struct Article: Identifiable {
    let id: UUID
    let title: String
    let content: String

    init(title: String, content: String) {
        self.id = UUID()
        self.title = title
        self.content = content
    }
}

// String identifier - useful for API models
struct Category: Identifiable {
    let id: String // API-provided identifier
    let name: String
    let icon: String
}

// Int identifier - database primary keys
struct DatabaseRecord: Identifiable {
    let id: Int
    let data: String
}

// Composite identifier - multiple properties
struct CourseEnrollment: Identifiable {
    struct ID: Hashable {
        let courseID: String
        let studentID: String
    }

    let id: ID
    let enrollmentDate: Date

    init(courseID: String, studentID: String, date: Date) {
        self.id = ID(courseID: courseID, studentID: studentID)
        self.enrollmentDate = date
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Identifiable with Enums&lt;/h2&gt;
&lt;p&gt;Enums can conform to Identifiable using self as the identifier:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;enum AppTab: String, Identifiable, CaseIterable {
    case home
    case search
    case profile
    case settings

    var id: String { rawValue }

    var title: String {
        rawValue.capitalized
    }

    var icon: String {
        switch self {
        case .home: return "house.fill"
        case .search: return "magnifyingglass"
        case .profile: return "person.fill"
        case .settings: return "gear"
        }
    }
}

// Usage in TabView
struct MainTabView: View {
    @State private var selectedTab: AppTab = .home

    var body: some View {
        TabView(selection: $selectedTab) {
            ForEach(AppTab.allCases) { tab in
                tabView(for: tab)
                    .tabItem {
                        Label(tab.title, systemImage: tab.icon)
                    }
                    .tag(tab)
            }
        }
    }

    @ViewBuilder
    func tabView(for tab: AppTab) -&amp;gt; some View {
        switch tab {
        case .home: HomeView()
        case .search: SearchView()
        case .profile: ProfileView()
        case .settings: SettingsView()
        }
    }
}

// Enum with associated values
enum LoadingState&amp;lt;T: Identifiable&amp;gt;: Identifiable {
    case idle
    case loading
    case success(T)
    case error(Error)

    var id: String {
        switch self {
        case .idle: return "idle"
        case .loading: return "loading"
        case .success(let data): return "success-\(data.id)"
        case .error: return "error"
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Extending Built-in Types&lt;/h2&gt;
&lt;p&gt;Extend existing types to conform to Identifiable:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;// Extend String to be Identifiable
extension String: @retroactive Identifiable {
    public var id: String { self }
}

// Now strings can be used directly in ForEach
struct TagsView: View {
    let tags: [String] = ["Swift", "iOS", "SwiftUI"]

    var body: some View {
        HStack {
            ForEach(tags) { tag in
                TagView(text: tag)
            }
        }
    }
}

// Extend Date for time-series data
extension Date: @retroactive Identifiable {
    public var id: TimeInterval {
        timeIntervalSince1970
    }
}

struct TimelineView: View {
    let events: [Date] = [
        Date(),
        Date().addingTimeInterval(3600),
        Date().addingTimeInterval(7200)
    ]

    var body: some View {
        List(events) { date in
            Text(date.formatted())
        }
    }
}

// Custom Color wrapper
struct IdentifiableColor: Identifiable {
    let id = UUID()
    let color: Color
    let name: String

    init(_ color: Color, name: String) {
        self.color = color
        self.name = name
    }
}

struct ColorPalette: View {
    let colors: [IdentifiableColor] = [
        IdentifiableColor(.red, name: "Red"),
        IdentifiableColor(.blue, name: "Blue"),
        IdentifiableColor(.green, name: "Green")
    ]

    var body: some View {
        HStack {
            ForEach(colors) { item in
                VStack {
                    Circle()
                        .fill(item.color)
                        .frame(width: 50, height: 50)
                    Text(item.name)
                }
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Identifiable in Collections&lt;/h2&gt;
&lt;p&gt;Use Identifiable for efficient collection operations:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct Task: Identifiable {
    let id: UUID
    var title: String
    var isCompleted: Bool

    init(title: String) {
        self.id = UUID()
        self.title = title
        self.isCompleted = false
    }
}

class TaskManager: ObservableObject {
    @Published var tasks: [Task] = []

    // Add task
    func add(_ task: Task) {
        tasks.append(task)
    }

    // Remove by ID
    func remove(id: UUID) {
        tasks.removeAll { $0.id == id }
    }

    // Update task
    func update(_ task: Task) {
        if let index = tasks.firstIndex(where: { $0.id == task.id }) {
            tasks[index] = task
        }
    }

    // Toggle completion
    func toggleCompletion(id: UUID) {
        if let index = tasks.firstIndex(where: { $0.id == id }) {
            tasks[index].isCompleted.toggle()
        }
    }

    // Reorder tasks
    func move(from source: IndexSet, to destination: Int) {
        tasks.move(fromOffsets: source, toOffset: destination)
    }
}

struct TaskListView: View {
    @StateObject private var taskManager = TaskManager()

    var body: some View {
        List {
            ForEach(taskManager.tasks) { task in
                TaskRow(task: task) {
                    taskManager.toggleCompletion(id: task.id)
                }
            }
            .onDelete { indexSet in
                indexSet.forEach { index in
                    taskManager.remove(id: taskManager.tasks[index].id)
                }
            }
            .onMove { source, destination in
                taskManager.move(from: source, to: destination)
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Identifiable with Navigation&lt;/h2&gt;
&lt;p&gt;Use Identifiable for type-safe navigation:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct Destination: Identifiable, Hashable {
    let id: String
    let title: String
    let content: String
}

struct NavigationExample: View {
    @State private var path: [Destination] = []

    let destinations: [Destination] = [
        Destination(id: "1", title: "Home", content: "Welcome"),
        Destination(id: "2", title: "Settings", content: "Configure"),
        Destination(id: "3", title: "About", content: "Info")
    ]

    var body: some View {
        NavigationStack(path: $path) {
            List(destinations) { destination in
                NavigationLink(value: destination) {
                    Text(destination.title)
                }
            }
            .navigationTitle("Destinations")
            .navigationDestination(for: Destination.self) { destination in
                DestinationView(destination: destination)
            }
        }
    }
}

struct DestinationView: View {
    let destination: Destination

    var body: some View {
        VStack {
            Text(destination.title)
                .font(.largeTitle)
            Text(destination.content)
        }
        .navigationTitle(destination.title)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Identifiable in SwiftData&lt;/h2&gt;
&lt;p&gt;Identifiable integrates seamlessly with SwiftData:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftData

@Model
final class Note: Identifiable {
    @Attribute(.unique) var id: UUID
    var title: String
    var content: String
    var createdAt: Date

    init(title: String, content: String) {
        self.id = UUID()
        self.title = title
        self.content = content
        self.createdAt = Date()
    }
}

struct NotesView: View {
    @Query private var notes: [Note]
    @Environment(\.modelContext) private var context

    var body: some View {
        List(notes) { note in
            NavigationLink(value: note) {
                VStack(alignment: .leading) {
                    Text(note.title)
                        .font(.headline)
                    Text(note.createdAt.formatted())
                        .font(.caption)
                }
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Advanced Patterns&lt;/h2&gt;
&lt;p&gt;Implement advanced patterns with Identifiable:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;// Identifiable with protocols
protocol Entity: Identifiable {
    var id: UUID { get }
    var createdAt: Date { get }
    var updatedAt: Date { get }
}

struct Document: Entity {
    let id: UUID
    var title: String
    var createdAt: Date
    var updatedAt: Date

    init(title: String) {
        self.id = UUID()
        self.title = title
        self.createdAt = Date()
        self.updatedAt = Date()
    }
}

// Generic container with Identifiable
struct DataContainer&amp;lt;T: Identifiable&amp;gt; {
    private var items: [T.ID: T] = [:]

    mutating func insert(_ item: T) {
        items[item.id] = item
    }

    mutating func remove(id: T.ID) {
        items.removeValue(forKey: id)
    }

    func get(id: T.ID) -&amp;gt; T? {
        items[id]
    }

    var all: [T] {
        Array(items.values)
    }
}

// Usage with any Identifiable type
var userContainer = DataContainer&amp;lt;User&amp;gt;()
userContainer.insert(User(name: "John", email: "john@example.com"))

var articleContainer = DataContainer&amp;lt;Article&amp;gt;()
articleContainer.insert(Article(title: "Swift", content: "Content"))&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Best Practices&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Use UUID for unique entities&lt;/strong&gt; - Guarantees uniqueness across systems&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Use natural keys when available&lt;/strong&gt; - API IDs, database keys, or domain identifiers&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Make IDs immutable&lt;/strong&gt; - Use &lt;code&gt;let&lt;/code&gt; to prevent accidental modification&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Avoid hash-based IDs for persistence&lt;/strong&gt; - Hash values aren't stable across sessions&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Use enums with rawValue&lt;/strong&gt; - Provides type-safe, stable identifiers&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Extend types carefully&lt;/strong&gt; - Consider uniqueness guarantees before extending&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Document ID meaning&lt;/strong&gt; - Make it clear what the ID represents&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Test identity logic&lt;/strong&gt; - Ensure IDs remain unique and stable&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The Identifiable protocol is a powerful tool for managing object identity in Swift and SwiftUI. By providing unique identification, it enables efficient collection operations, seamless SwiftUI integration, and type-safe navigation. Understanding and effectively using Identifiable is essential for building robust, maintainable iOS applications.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swift/identifiable"&gt;Identifiable Protocol&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/list"&gt;SwiftUI List&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/foundation/uuid"&gt;UUID Documentation&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Swift Package: SwiftCronParser</title>    <link>https://wesleydegroot.nl/blog/swift-package-swiftcronparser</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/swift-package-swiftcronparser</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:43 +0200</pubDate>
    <category>SwiftPM</category>
    <category>SwiftCronParser</category>
    <description>&lt;p&gt;In this post, we will explore the &lt;code&gt;SwiftCronParser&lt;/code&gt; Swift Package, a package to parse cron expressions in Swift.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;SwiftCronParser&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;SwiftCronParser&lt;/code&gt; is a Swift package that provides a simple and efficient way to parse cron expressions. Cron expressions are widely used in scheduling tasks and jobs in Unix-like operating systems. With &lt;code&gt;SwiftCronParser&lt;/code&gt;, you can easily work with cron expressions in your Swift applications.&lt;/p&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;p&gt;To install &lt;code&gt;SwiftCronParser&lt;/code&gt;, you can use the Swift Package Manager. Add the following line to your &lt;code&gt;Package.swift&lt;/code&gt; file:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;.package(url: "https://github.com/0xWDG/SwiftCronParser", branch: "main")&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, add &lt;code&gt;SwiftCronParser&lt;/code&gt; to the dependencies of your target:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;.target(
    name: "YourTarget",
    dependencies: ["SwiftCronParser"]
)&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Usage:&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI
import SwiftCronParser

let cronParser = SwiftCronParser(input: "0 0 * * *")
let cronTime = cronParser.parse()

if let error = cronTime.error {
    print("Error: \(error)")
} else {
    print("Cron string: \(cronTime)") // 0 0 0,1,2,3,4,5,6 1,2,3,4,5,6,7,8,9,10,11,12 0,1,2,3,4,5,6
    // Or using cronTime.minute, cronTime.hour, cronTime.dayOfMonth, cronTime.month, cronTime.dayOfWeek
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In this post, we explored the &lt;code&gt;SwiftCronParser&lt;/code&gt; Swift Package, a powerful tool for parsing cron expressions in Swift. We covered the installation process, usage examples, and potential use cases for this package. With &lt;code&gt;SwiftCronParser&lt;/code&gt;, you can easily work with cron expressions and integrate them into your Swift applications.&lt;/p&gt;
&lt;p&gt;Resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/0xWDG/SwiftCronParser"&gt;https://github.com/0xWDG/SwiftCronParser&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://swift.org/package-manager/"&gt;https://swift.org/package-manager/&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Swift Package: SwiftExtras</title>    <link>https://wesleydegroot.nl/blog/swift-package-swiftextras</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/swift-package-swiftextras</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:43 +0200</pubDate>
    <category>SwiftPM</category>
    <category>SwiftExtras</category>
    <description>&lt;p&gt;In this post, we will explore the &lt;code&gt;SwiftExtras&lt;/code&gt; Swift Package, a package that provides additional functionality and utilities for Swift developers.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;SwiftExtras&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;SwiftExtras&lt;/code&gt; is a Swift package that offers a collection of useful extensions, utilities, and helper functions to enhance the Swift programming experience. It aims to fill in the gaps and provide commonly needed functionality that may not be available in the standard library.&lt;/p&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;p&gt;To install &lt;code&gt;SwiftExtras&lt;/code&gt;, you can use the Swift Package Manager. Add the following line to your &lt;code&gt;Package.swift&lt;/code&gt; file:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;.package(url: "https://github.com/0xWDG/SwiftExtras", branch: "main")&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, add &lt;code&gt;SwiftExtras&lt;/code&gt; to the dependencies of your target:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;.target(
    name: "YourTarget",
    dependencies: ["SwiftExtras"]
)&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Features&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Versatile Extensions &amp;amp; Helpers&lt;/strong&gt;&lt;br&gt;
SwiftExtras offers enhancements across a wide range of Swift types as &lt;code&gt;String&lt;/code&gt;, &lt;code&gt;Date&lt;/code&gt;, &lt;code&gt;Color&lt;/code&gt;, &lt;code&gt;View&lt;/code&gt;, &lt;code&gt;Error&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cross-Platform SwiftUI Utilities&lt;/strong&gt;&lt;br&gt;
The package includes ready-to-use SwiftUI components like &lt;code&gt;IndexedList&lt;/code&gt;, &lt;code&gt;MonthYearPickerView&lt;/code&gt;, and other layout structures—built for seamless integration across iOS, macOS, tvOS, and watchOS.&lt;/p&gt;
&lt;p&gt;e.g. but not limited to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;IndexedList(data:rowContent:)&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Error.errorCode&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;AppInfo.openAppStorePage()&lt;/code&gt; and &lt;code&gt;AppInfo.isLatestVersion()&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;String.clean()&lt;/code&gt; (for removing diacritics)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;.longPress(action:)&lt;/code&gt; for views&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;MonthYearPickerView()&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;String.containsHTML&lt;/code&gt; &amp;amp; &lt;code&gt;String.htmlToAttributedString()&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Read the &lt;a href="https://0xwdg.github.io/SwiftExtras/"&gt;documentation&lt;/a&gt; for more information on how to use these features.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In this post, we explored the &lt;code&gt;SwiftExtras&lt;/code&gt; Swift Package, a powerful tool for enhancing the Swift programming experience. We covered the installation process, usage examples, and potential use cases for this package. With &lt;code&gt;SwiftExtras&lt;/code&gt;, you can easily work with common tasks and integrate them into your Swift applications.&lt;/p&gt;
&lt;p&gt;Resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/0xWDG/SwiftExtras"&gt;https://github.com/0xWDG/SwiftExtras&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://swift.org/package-manager/"&gt;https://swift.org/package-manager/&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Quick actions in SwiftUI</title>    <link>https://wesleydegroot.nl/blog/quick-actions-for-swiftui</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/quick-actions-for-swiftui</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:44 +0200</pubDate>
    <category>SwiftUI</category>
    <category>QuickActions</category>
    <description>&lt;p&gt;Quick actions are a powerful feature in SwiftUI that allows developers to add contextually relevant actions to their views. These actions can be triggered by user interactions, such as tapping and holding on a view, and can provide a more streamlined and efficient user experience.&lt;/p&gt;
&lt;h2&gt;Static Quick Actions&lt;/h2&gt;
&lt;p&gt;Static quick actions are predefined actions that can be added to a view without requiring any additional configuration. These actions are typically used for common tasks, such as sharing content or navigating to a specific screen.&lt;/p&gt;
&lt;h3&gt;Defining Quick Actions&lt;/h3&gt;
&lt;p&gt;We are going to add some information to the &lt;a href="https://developer.apple.com/documentation/bundleresources/information-property-list"&gt;&lt;code&gt;Info.plist&lt;/code&gt;&lt;/a&gt; file:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/bundleresources/information-property-list/uiapplicationshortcutitems/uiapplicationshortcutitemtype"&gt;&lt;code&gt;UIApplicationShortcutItemType&lt;/code&gt;&lt;/a&gt; – A unique identifier for the action, used later when handling the action.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/bundleresources/information-property-list/uiapplicationshortcutitems/uiapplicationshortcutitemiconsymbolname"&gt;&lt;code&gt;UIApplicationShortcutItemIconSymbolName&lt;/code&gt;&lt;/a&gt; – Specifies an SF Symbol for the action’s icon.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/bundleresources/information-property-list/uiapplicationshortcutitems/uiapplicationshortcutitemtitle"&gt;&lt;code&gt;UIApplicationShortcutItemTitle&lt;/code&gt;&lt;/a&gt; – Displays the action’s title.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class="language-plist"&gt;&amp;lt;key&amp;gt;UIApplicationShortcutItems&amp;lt;/key&amp;gt;
&amp;lt;array&amp;gt;
    &amp;lt;dict&amp;gt;
        &amp;lt;key&amp;gt;UIApplicationShortcutItemType&amp;lt;/key&amp;gt;
        &amp;lt;string&amp;gt;favoritesAction&amp;lt;/string&amp;gt;
        &amp;lt;key&amp;gt;UIApplicationShortcutItemIconSymbolName&amp;lt;/key&amp;gt;
        &amp;lt;string&amp;gt;heart&amp;lt;/string&amp;gt;
        &amp;lt;key&amp;gt;UIApplicationShortcutItemTitle&amp;lt;/key&amp;gt;
        &amp;lt;string&amp;gt;Favorites&amp;lt;/string&amp;gt;
    &amp;lt;/dict&amp;gt;
&amp;lt;/array&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Tip: To localize the title, you can add the value from &lt;a href="https://developer.apple.com/documentation/bundleresources/information-property-list/uiapplicationshortcutitems/uiapplicationshortcutitemtitle"&gt;&lt;code&gt;UIApplicationShortcutItemTitle&lt;/code&gt;&lt;/a&gt; to your &lt;a href="https://developer.apple.com/documentation/xcode/localizing-and-varying-text-with-a-string-catalog"&gt;localization&lt;/a&gt; file.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Dynamic Quick Actions&lt;/h2&gt;
&lt;p&gt;This is an example of how to update quick actions dynamically based on the app's state.&lt;br&gt;
We use &lt;a href="https://developer.apple.com/documentation/uikit/uiapplicationshortcutitem"&gt;&lt;code&gt;UIApplicationShortcutItem&lt;/code&gt;&lt;/a&gt; to create a quick action item, and then we update the quick actions based on the app's state, such as when the user adds or removes items from their favorites.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;func updateQuickActionItems() {
    // Check if there are any favorite items, otherwise don't return quick actions
    guard !favorites.isEmpty else {
        // Tell the system about the new quick actions (no actions)
        UIApplication.shared.shortcutItems = []
        return
    }

    // Create a quick action for the favorites
    let favoritesAction = UIApplicationShortcutItem(
        type: "favoritesAction",
        localizedTitle: "Favorites",
        localizedSubtitle: nil,
        icon: UIApplicationShortcutIcon(systemImageName: "heart"),
        userInfo: [:]
    )

    // Tell the system about the new quick actions
    UIApplication.shared.shortcutItems = [favoritesAction]
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: You need to call &lt;code&gt;updateQuickActionItems()&lt;/code&gt; on a convenient place, i'll suggest to call it when the favorites list is updated.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Handling Quick Actions&lt;/h2&gt;
&lt;p&gt;SwiftUI unfortunately does not provide a built-in way to handle quick actions directly. However, we can use the AppDelegate and SceneDelegate to manage quick actions.&lt;/p&gt;
&lt;p&gt;We need to use a lot of UIKit integration to make this work. Here's how you can set it up:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/uiapplicationdelegateadaptor"&gt;&lt;code&gt;@UIApplicationDelegateAdaptor&lt;/code&gt;&lt;/a&gt; to tell SwiftUI that we want to use &lt;code&gt;AppDelegate&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uiapplicationdelegate"&gt;&lt;code&gt;AppDelegate&lt;/code&gt;&lt;/a&gt; to manage the application lifecycle&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uisceneconfiguration"&gt;&lt;code&gt;UISceneConfiguration&lt;/code&gt;&lt;/a&gt; to configure the app's scenes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uiwindowscenedelegate"&gt;&lt;code&gt;UIWindowSceneDelegate&lt;/code&gt;&lt;/a&gt; to manage the app's windows&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;@main
struct MyApp: App {
    // Set up delegate adaptor
    @UIApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

// Set up AppDelegate
class AppDelegate: NSObject, UIApplicationDelegate {
    // Register SceneDelegate
    func application(
        _ application: UIApplication,
        configurationForConnecting connectingSceneSession: UISceneSession,
        options: UIScene.ConnectionOptions
    ) -&amp;gt; UISceneConfiguration {
        let configuration = UISceneConfiguration(
            name: nil,
            sessionRole: connectingSceneSession.role
        )
        if connectingSceneSession.role == .windowApplication {
            configuration.delegateClass = SceneDelegate.self
        }
        return configuration
    }
}

// Set up SceneDelegate
class SceneDelegate: NSObject, UIWindowSceneDelegate {
    // Handle quick action (fresh start)
    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        if let shortcutItem = connectionOptions.shortcutItem {
            handleShortcut(shortcutItem)
        }
    }

    // Handle quick action (resuming app)
    func windowScene(
        _ windowScene: UIWindowScene,
        performActionFor shortcutItem: UIApplicationShortcutItem,
        completionHandler: @escaping (Bool) -&amp;gt; Void
    ) {
        handleShortcut(shortcutItem)
        completionHandler(true)
    }

    // Handle quick action
    func handleShortcut(_ item: UIApplicationShortcutItem) {
        if item.type == "favoritesAction" {
            print("Show favorites")
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Quick actions do not work great with SwiftUI it requires a lot on UIKit integration.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You need to manage the application lifecycle and window scenes manually.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;In this article, we explored how to implement quick actions in a SwiftUI application. We covered static quick actions defined in the app's Info.plist file, as well as dynamic quick actions that can be updated at runtime. We also discussed how to handle quick actions using the AppDelegate and SceneDelegate.&lt;/p&gt;
&lt;p&gt;Resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uiapplicationshortcutitem"&gt;https://developer.apple.com/documentation/uikit/uiapplicationshortcutitem&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/xcode/localizing-and-varying-text-with-a-string-catalog"&gt;https://developer.apple.com/documentation/xcode/localizing-and-varying-text-with-a-string-catalog&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/uiapplicationdelegateadaptor"&gt;https://developer.apple.com/documentation/swiftui/uiapplicationdelegateadaptor&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uiapplicationdelegate"&gt;https://developer.apple.com/documentation/uikit/uiapplicationdelegate&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uisceneconfiguration"&gt;https://developer.apple.com/documentation/uikit/uisceneconfiguration&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uiwindowscenedelegate"&gt;https://developer.apple.com/documentation/uikit/uiwindowscenedelegate&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Picker in SwiftUI</title>    <link>https://wesleydegroot.nl/blog/swiftui-picker</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/swiftui-picker</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:44 +0200</pubDate>
    <category>SwiftUI</category>
    <category>Picker</category>
    <description>&lt;p&gt;The &lt;code&gt;Picker&lt;/code&gt; is a SwiftUI view that presents a set of options for the user to choose from. It can be displayed as a dropdown menu, a segmented control, or a wheel, depending on the context and the number of options available.&lt;/p&gt;
&lt;h2&gt;Use Case: Picker&lt;/h2&gt;
&lt;p&gt;A common use case for &lt;code&gt;Picker&lt;/code&gt; is to allow users to select a value from a predefined list. For example, you might use a &lt;code&gt;Picker&lt;/code&gt; to let users choose their favorite fruit from a list of options.&lt;/p&gt;
&lt;h2&gt;Set up Picker cases&lt;/h2&gt;
&lt;p&gt;We need to define the options for our picker. In this case, we'll create an enum for the different themes.&lt;/p&gt;
&lt;p&gt;We conform it to &lt;a href="https://developer.apple.com/documentation/swift/string"&gt;&lt;code&gt;String&lt;/code&gt;&lt;/a&gt; and add conformance to &lt;a href="https://developer.apple.com/documentation/swift/caseiterable"&gt;&lt;code&gt;CaseIterable&lt;/code&gt;&lt;/a&gt; (to support .allCases) and &lt;a href="https://developer.apple.com/documentation/swift/identifiable"&gt;&lt;code&gt;Identifiable&lt;/code&gt;&lt;/a&gt; (so that we can use it in a &lt;a href="https://developer.apple.com/documentation/swiftui/forEach"&gt;&lt;code&gt;ForEach&lt;/code&gt;&lt;/a&gt; loop)&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;enum Theme: String, CaseIterable, Identifiable {
    case light = "Light"
    case dark = "Dark"
    case system = "System Default"

    var id: String { self.rawValue }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Picker View&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct PickerView: View {
    @State private var selectedTheme: Theme = .light
    var body: some View {
        Form {
            Section("Default/Automatic") {
                Picker("Select Theme", selection: $selectedTheme) {
                    ForEach(Theme.allCases) { theme in
                        Text(theme.rawValue)
                            .tag(theme)
                    }
                }
                .pickerStyle(.automatic)
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Different Picker styles&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;Picker&lt;/code&gt; can be styled in various ways to suit different use cases. Here are some of the available styles:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Style&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/pickerstyle/automatic"&gt;.automatic&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;The system chooses the best style based on the context.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/pickerstyle/menu"&gt;.menu&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A menu-style picker that presents options in a list.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/pickerstyle/segmented"&gt;.segmented&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A segmented control style picker for a compact selection.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/pickerstyle/navigationlink"&gt;.navigationLink&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A picker that navigates to a new view for selection.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/pickerstyle/palette"&gt;.palette&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A color palette style picker for selecting colors.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/pickerstyle/inline"&gt;.inline&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;An inline style picker that displays options within the form.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/pickerstyle/wheel"&gt;.wheel&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A wheel-style picker for selecting values in a scrollable wheel.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;In this code example, we create a &lt;code&gt;PickerView&lt;/code&gt; that demonstrates the different picker styles:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct PickerView: View {
    @State private var selectedTheme: Theme = .light
    var body: some View {
        NavigationStack {
            Form {
                Section("Default/Automatic") {
                    Picker("Select Theme", selection: $selectedTheme) {
                        ForEach(Theme.allCases) { theme in
                            Text(theme.rawValue)
                                .tag(theme)
                        }
                    }
                    .pickerStyle(.automatic)
                }

                Section(".menu") {
                    Picker("Select Theme", selection: $selectedTheme) {
                        ForEach(Theme.allCases) { theme in
                            Text(theme.rawValue).tag(theme)
                        }
                    }
                    .pickerStyle(.menu)
                }

                Section(".segmented") {
                    Picker("Select Theme", selection: $selectedTheme) {
                        ForEach(Theme.allCases) { theme in
                            Text(theme.rawValue).tag(theme)
                        }
                    }
                    .pickerStyle(.segmented)
                }

                Section(".navigationLink") {
                    Picker("Select Theme", selection: $selectedTheme) {
                        ForEach(Theme.allCases) { theme in
                            Text(theme.rawValue).tag(theme)
                        }
                    }
                    .pickerStyle(.navigationLink)
                }

                Section(".palette") {
                    Picker("Select Theme", selection: $selectedTheme) {
                        ForEach(Theme.allCases) { theme in
                            Text(theme.rawValue).tag(theme)
                        }
                    }
                    .pickerStyle(.palette)
                }

                Section(".inline") {
                    Picker("Select Theme", selection: $selectedTheme) {
                        ForEach(Theme.allCases) { theme in
                            Text(theme.rawValue).tag(theme)
                        }
                    }
                    .pickerStyle(.inline)
                }

                Section(".inline &amp;amp; labelsHidden") {
                    Picker("Select Theme", selection: $selectedTheme) {
                        ForEach(Theme.allCases) { theme in
                            Text(theme.rawValue).tag(theme)
                        }
                    }
                    .pickerStyle(.inline)
                    // I'd suggest to hide the "labels" it looks cleaner when using .inline
                    .labelsHidden()
                }

                Section(".wheel") {
                    Picker("Select Theme", selection: $selectedTheme) {
                        ForEach(Theme.allCases) { theme in
                            Text(theme.rawValue).tag(theme)
                        }
                    }
                    .pickerStyle(.wheel)
                }
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a screenshot of the different picker styles in action.&lt;br&gt;
&lt;img src="/resources/Picker.jpg" alt="Picker" loading="lazy"&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Pickers are a powerful way to allow users to select from a range of options in a SwiftUI application. By utilizing the various picker styles available, you can create a more intuitive and user-friendly interface.&lt;/p&gt;
&lt;p&gt;Resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui"&gt;https://developer.apple.com/documentation/swiftui&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/picker"&gt;https://developer.apple.com/documentation/swiftui/picker&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/pickerstyle"&gt;https://developer.apple.com/documentation/swiftui/pickerstyle&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/forEach"&gt;https://developer.apple.com/documentation/swiftui/forEach&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/state"&gt;https://developer.apple.com/documentation/swiftui/state&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/pickerstyle/automatic"&gt;https://developer.apple.com/documentation/swiftui/pickerstyle/automatic&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/pickerstyle/menu"&gt;https://developer.apple.com/documentation/swiftui/pickerstyle/menu&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/pickerstyle/segmented"&gt;https://developer.apple.com/documentation/swiftui/pickerstyle/segmented&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/pickerstyle/navigationlink"&gt;https://developer.apple.com/documentation/swiftui/pickerstyle/navigationlink&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/pickerstyle/palette"&gt;https://developer.apple.com/documentation/swiftui/pickerstyle/palette&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/pickerstyle/inline"&gt;https://developer.apple.com/documentation/swiftui/pickerstyle/inline&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/pickerstyle/wheel"&gt;https://developer.apple.com/documentation/swiftui/pickerstyle/wheel&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Monospace digits</title>    <link>https://wesleydegroot.nl/blog/monospace-digits</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/monospace-digits</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:45 +0200</pubDate>
    <category>SwiftUI</category>
    <category>Monospace</category>
    <description>&lt;p&gt;Monospace digits are a type of font where each character takes up the same amount of horizontal space. This is particularly useful in programming and data presentation, as it allows for better alignment and readability of numbers.&lt;/p&gt;
&lt;h2&gt;Use Case: Monospace Digits&lt;/h2&gt;
&lt;p&gt;If you have a countdown timer in your app, using only &lt;code&gt;Text&lt;/code&gt; will cause the digits to be misaligned as they change. By using a monospace font, you can ensure that each digit takes up the same amount of space, making the timer easier to read.&lt;/p&gt;
&lt;h2&gt;Example&lt;/h2&gt;
&lt;p&gt;Here's a simple example of how to use monospace digits in a SwiftUI countdown timer:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct AppTimer: View {
    @State private var timeRunning = 0

    var body: some View {
        Text("The app is running for \(timeRunning) seconds.")
            .font(.title2)
            .monospacedDigit()
            .task {
                repeat {
                    try? await Task.sleep(for: .seconds(1))
                    timeRunning += 1
                } while true
            }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code ensures that the timer updates every second without changing the text alignment.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;repeat&lt;/code&gt; block allows to repeat a certain action (in this case, updating the timer) while a certain condition is met (in this case, forever).&lt;/p&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;p&gt;While monospace digits can improve readability, they may not always be the best choice for every design. Consider the overall aesthetic of your app and whether a monospace font fits with the other design elements.&lt;/p&gt;
&lt;h2&gt;Wrap Up&lt;/h2&gt;
&lt;p&gt;Using monospace digits in your SwiftUI app can enhance the readability of numerical data, especially in dynamic contexts like timers or counters. By ensuring consistent spacing for each digit, you can create a more visually appealing and user-friendly interface.&lt;/p&gt;</description>
  </item>
  <item>
    <title>Environment: OpenURL</title>    <link>https://wesleydegroot.nl/blog/environment-openurl</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/environment-openurl</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:45 +0200</pubDate>
    <category>Environment</category>
    <category>OpenURL</category>
    <description>&lt;p&gt;&lt;code&gt;@Environment(\.openURL)&lt;/code&gt; allows you to open URLs from your SwiftUI views.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;@Environment(\.openURL)&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/environmentvalues/openurl"&gt;&lt;code&gt;@Environment(\.openURL)&lt;/code&gt;&lt;/a&gt; is a property wrapper that provides a way to open URLs in your SwiftUI application. It gives you access to the &lt;a href="https://developer.apple.com/documentation/swiftui/environmentvalues/openurl"&gt;&lt;code&gt;OpenURL&lt;/code&gt;&lt;/a&gt; environment value, which you can use to present a URL in the appropriate way for the current platform.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct ContentView: View {
    @Environment(\.openURL) var openURL

    var body: some View {
        Button("Open Website") {
            openURL(URL(string: "https://wesleydegroot.nl")!)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Custom &lt;code&gt;OpenURLAction&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;You can also set a custom &lt;a href="https://developer.apple.com/documentation/swiftui/openurlaction"&gt;&lt;code&gt;OpenURLAction&lt;/code&gt;&lt;/a&gt; for your views. This allows you to define how URLs should be handled when they are opened.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct ContentView: View {
    var body: some View {
        Text(.init("Visit https://wesleydegroot.nl or [Play a sound](play://sound)"))
        .environment(\.openURL, OpenURLAction { url in
            if let scheme = url.scheme {
                if scheme == "play" {
                    // MusicPlayer.play(url)
                    return .handled
                }
            }
            return .systemAction
        })
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;Opening URLs in SwiftUI is made easy with &lt;a href="https://developer.apple.com/documentation/swiftui/environmentvalues/openurl"&gt;&lt;code&gt;@Environment(\.openURL)&lt;/code&gt;&lt;/a&gt;. This powerful property wrapper allows you to handle URL actions seamlessly across different platforms.&lt;/p&gt;
&lt;p&gt;Resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/environment"&gt;https://developer.apple.com/documentation/swiftui/environment&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/environmentvalues/openurl"&gt;https://developer.apple.com/documentation/swiftui/environmentvalues/openurl&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/openurlaction"&gt;https://developer.apple.com/documentation/swiftui/openurlaction&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Make your app visible with alternative app names</title>    <link>https://wesleydegroot.nl/blog/inalternativeappnames</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/inalternativeappnames</guid>
    <pubDate>Tue, 02 Dec 2025 21:18:45 +0100</pubDate>
    <category>INAlternativeAppNames</category>
    <description>&lt;p&gt;With &lt;code&gt;INAlternativeAppNames&lt;/code&gt;, you can provide alternative names for your app so that the user can find your app more easily.&lt;/p&gt;
&lt;h2&gt;Example you have a agenda app called &lt;code&gt;Calendo&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;You can provide alternative names like &lt;code&gt;Calendar&lt;/code&gt;, &lt;code&gt;Kalender&lt;/code&gt; or &lt;code&gt;Agenda&lt;/code&gt; to help users discover your app through different keywords.&lt;/p&gt;
&lt;h2&gt;How to implement&lt;/h2&gt;
&lt;p&gt;Add the &lt;a href="https://developer.apple.com/documentation/sirikit/specifying-synonyms-for-your-app-name"&gt;&lt;code&gt;INAlternativeAppNames&lt;/code&gt;&lt;/a&gt; key to your app's &lt;a href="https://developer.apple.com/documentation/bundleresources/information_property_list"&gt;&lt;code&gt;Info.plist&lt;/code&gt;&lt;/a&gt; file and provide an array of alternative names.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-xml"&gt;&amp;lt;key&amp;gt;INAlternativeAppNames&amp;lt;/key&amp;gt;
&amp;lt;array&amp;gt;
    &amp;lt;dict&amp;gt;
        &amp;lt;key&amp;gt;INAlternativeAppName&amp;lt;/key&amp;gt;
        &amp;lt;string&amp;gt;Kalender&amp;lt;/string&amp;gt;
        &amp;lt;key&amp;gt;INAlternativeAppNamePronunciationHint&amp;lt;/key&amp;gt;
        &amp;lt;string&amp;gt;Kalender&amp;lt;/string&amp;gt;
    &amp;lt;/dict&amp;gt;
    &amp;lt;dict&amp;gt;
        &amp;lt;key&amp;gt;INAlternativeAppName&amp;lt;/key&amp;gt;
        &amp;lt;string&amp;gt;Calendar&amp;lt;/string&amp;gt;
        &amp;lt;key&amp;gt;INAlternativeAppNamePronunciationHint&amp;lt;/key&amp;gt;
        &amp;lt;string&amp;gt;Calendar&amp;lt;/string&amp;gt;
    &amp;lt;/dict&amp;gt;
    &amp;lt;dict&amp;gt;
        &amp;lt;key&amp;gt;INAlternativeAppName&amp;lt;/key&amp;gt;
        &amp;lt;string&amp;gt;Planning&amp;lt;/string&amp;gt;
        &amp;lt;key&amp;gt;INAlternativeAppNamePronunciationHint&amp;lt;/key&amp;gt;
        &amp;lt;string&amp;gt;Planning&amp;lt;/string&amp;gt;
    &amp;lt;/dict&amp;gt;
&amp;lt;/array&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The end user should be able to find your app using the app name and any of the alternative names you provided.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; There is a limit of 3 alternative names that you can provide.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;In this article, we explored how to use &lt;code&gt;INAlternativeAppNames&lt;/code&gt; to make your app more discoverable through alternative names. By providing a variety of keywords, you can help users find your app more easily.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/sirikit/specifying-synonyms-for-your-app-name"&gt;https://developer.apple.com/documentation/sirikit/specifying-synonyms-for-your-app-name&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Tracking Screen Views in SwiftUI with a Custom ViewModifier</title>    <link>https://wesleydegroot.nl/blog/tracking-screen-views-in-swiftui-with-a-custom-viewmodifier</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/tracking-screen-views-in-swiftui-with-a-custom-viewmodifier</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:46 +0200</pubDate>
    <category>SwiftUI</category>
    <category>ViewModifier</category>
    <category>Analytics</category>
    <description>&lt;p&gt;In this post we'll create a small extension to track which screens are being viewed in a SwiftUI application.&lt;/p&gt;
&lt;h2&gt;Why Track Screen Views?&lt;/h2&gt;
&lt;p&gt;Tracking screen views is essential for understanding user behavior within your app. By knowing which screens are viewed most often, you can make informed decisions about where to focus your development efforts, improve user experience, and ultimately drive engagement.&lt;/p&gt;
&lt;h2&gt;TelemetryDeck Integration&lt;/h2&gt;
&lt;p&gt;For This example we'll use &lt;a href="https://telemetrydeck.com"&gt;TelemetryDeck&lt;/a&gt;.&lt;br&gt;
You can create a &lt;a href="https://dashboard.telemetrydeck.com/registration/organization?referralCode=0XL7FMWOV3LYPR61"&gt;free account&lt;/a&gt;(referral) and get your API key from the dashboard.&lt;br&gt;
p.s. if you use my referral code (&lt;code&gt;0XL7FMWOV3LYPR61&lt;/code&gt;) we'll both get 100,000 extra signals every month 🙌&lt;/p&gt;
&lt;h2&gt;Why TelemetryDeck?&lt;/h2&gt;
&lt;p&gt;TelemetryDeck is a powerful tool for tracking user interactions in your app. It provides a privacy-first approach to analytics, ensuring that user data is anonymized.&lt;/p&gt;
&lt;h2&gt;Setting up TelemetryDeck&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI
import TelemetryDeck

@main
struct YourAppNameApp: App {
    init() {
        let config = TelemetryDeck.Config(appID: "YOUR-APP-ID")
        TelemetryDeck.initialize(config: config)
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Use Case: Tracking Screen Views&lt;/h2&gt;
&lt;p&gt;To track screen views, we can create a custom ViewModifier that will automatically log the screen name whenever a view appears. Here's a simple implementation:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI
import TelemetryDeck

struct ContentView: View {
    var body: some View {
        Text("Hello, World!")
            .screenViewEvent()
    }
}

extension View {
    func screenViewEvent(
        _ screen: String = #file,
        extraParameters: [String: String]? = nil
    ) -&amp;gt; some View {
        self.onAppear {
            TelemetryDeck.signal(
                "open.\(screen)",
                parameters: extraParameters ?? [:]
            )
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;In this post, we've created a simple yet effective way to track screen views in a SwiftUI application using a custom ViewModifier. By integrating with TelemetryDeck, we can gain valuable insights into user behavior and make data-driven decisions to improve our app.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://docs.telemetrydeck.com/"&gt;TelemetryDeck Documentation&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://dashboard.telemetrydeck.com/registration/organization?referralCode=0XL7FMWOV3LYPR61"&gt;free account&lt;/a&gt; (referral)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Task.sleep() vs. Task.yield(): The differences explained</title>    <link>https://wesleydegroot.nl/blog/task-sleep-vs-task-yield-the-differences-explained</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/task-sleep-vs-task-yield-the-differences-explained</guid>
    <pubDate>Mon, 22 Dec 2025 11:52:34 +0100</pubDate>
    <category>Swift</category>
    <category>Task</category>
    <description>&lt;p&gt;Task.sleep() vs. Task.yield() The differences explained&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;Task.sleep()&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;With &lt;a href="https://developer.apple.com/documentation/swift/task/sleep(for:tolerance:clock:)"&gt;&lt;code&gt;Task.sleep()&lt;/code&gt;&lt;/a&gt;, you can suspend the execution of a task for a specified duration. This is useful when you want to introduce a delay in your asynchronous code, such as waiting for a certain condition to be met or simulating a long-running operation.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;Task.yield()&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;With &lt;a href="https://developer.apple.com/documentation/swift/task/yield%28%29"&gt;&lt;code&gt;Task.yield()&lt;/code&gt;&lt;/a&gt;, you can voluntarily yield control back to the system, allowing other tasks to run. This is useful in scenarios where you want to prevent blocking the current &lt;a href="https://mastodon.social/@mattiem/115728595787924474"&gt;current actor&lt;/a&gt; and allow for more responsive and efficient multitasking.&lt;/p&gt;
&lt;h2&gt;When should you use &lt;code&gt;Task.sleep()&lt;/code&gt; vs &lt;code&gt;Task.yield()&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;Both methods suspend execution, but they have a few key differences.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://developer.apple.com/documentation/swift/task/sleep(for:tolerance:clock:)"&gt;&lt;code&gt;Task.sleep()&lt;/code&gt;&lt;/a&gt; method suspends execution for a set of time while the &lt;a href="https://developer.apple.com/documentation/swift/task/yield%28%29"&gt;&lt;code&gt;Task.yield()&lt;/code&gt;&lt;/a&gt; method might only suspend if other tasks with similar or lower priority await execution. Therefore, the duration of suspension is only fixed for &lt;a href="https://developer.apple.com/documentation/swift/task/sleep(for:tolerance:clock:)"&gt;&lt;code&gt;Task.sleep()&lt;/code&gt;&lt;/a&gt; and indeterminate for &lt;a href="https://developer.apple.com/documentation/swift/task/yield%28%29"&gt;&lt;code&gt;Task.yield()&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://developer.apple.com/documentation/swift/task/sleep(for:tolerance:clock:)"&gt;&lt;code&gt;Task.sleep()&lt;/code&gt;&lt;/a&gt; method is interruptible via cancellation, while &lt;a href="https://developer.apple.com/documentation/swift/task/yield%28%29"&gt;&lt;code&gt;Task.yield()&lt;/code&gt;&lt;/a&gt; only yields control. Both are non-blocking for their respective threads.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In conclusion, &lt;a href="https://developer.apple.com/documentation/swift/task/sleep(for:tolerance:clock:)"&gt;&lt;code&gt;Task.sleep()&lt;/code&gt;&lt;/a&gt; is best used when you need a precise delay, while &lt;a href="https://developer.apple.com/documentation/swift/task/yield%28%29"&gt;&lt;code&gt;Task.yield()&lt;/code&gt;&lt;/a&gt; is more appropriate for allowing other tasks to run without blocking. Understanding these differences can help you write more efficient and responsive asynchronous code.&lt;/p&gt;
&lt;h2&gt;Credits:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Thanks &lt;a href="https://www.massicotte.org/"&gt;Matt Massicotte&lt;/a&gt; for pointing me out on a mistake in &lt;a href="https://mastodon.social/@mattiem/115728595787924474"&gt;this post&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href='https://developer.apple.com/documentation/swift/task/sleep(for:tolerance:clock:)'&gt;&lt;a href="https://developer.apple.com/documentation/swift/task/sleep(for:tolerance:clock"&gt;https://developer.apple.com/documentation/swift/task/sleep(for:tolerance:clock&lt;/a&gt;:)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href='https://developer.apple.com/documentation/swift/task/yield()'&gt;&lt;a href="https://developer.apple.com/documentation/swift/task/yield"&gt;https://developer.apple.com/documentation/swift/task/yield&lt;/a&gt;()&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Form in SwiftUI</title>    <link>https://wesleydegroot.nl/blog/swiftui-form</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/swiftui-form</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:48 +0200</pubDate>
    <category>SwiftUI</category>
    <category>Form</category>
    <description>&lt;p&gt;User input powers almost every app. From reminders to events, contacts, or sign-ups thats where &lt;code&gt;Form&lt;/code&gt; comes in.&lt;/p&gt;
&lt;h2&gt;A Basic Contact Form&lt;/h2&gt;
&lt;p&gt;The simplest way to start is by grouping &lt;a href="https://developer.apple.com/documentation/swiftui/textfield"&gt;&lt;code&gt;TextField&lt;/code&gt;&lt;/a&gt; inside a &lt;a href="https://developer.apple.com/documentation/swiftui/form"&gt;&lt;code&gt;Form&lt;/code&gt;&lt;/a&gt;. SwiftUI automatically handles grouping, scrolling, and native styling.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;Form {
    Section {
        TextField("First name", text: $firstName)
        TextField("Last name", text: $lastName)
        TextField("Company", text: $company)
    }
    Section {
        TextField("Phone", text: $phone)
    }
    Section {
        TextField("Email", text: $email)
    }
}
.navigationTitle("New Contact")
.padding()&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Adding Toggles &amp;amp; Pickers&lt;/h2&gt;
&lt;p&gt;Forms are not just text fields. You can combine &lt;strong&gt;toggles, pickers, and sliders&lt;/strong&gt; seamlessly.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;Form {
    Section("Preferences") {
        Toggle("Enable notifications", isOn: $notificationsEnabled)
        Picker("Theme", selection: $theme) {
            Text("Light").tag("light")
            Text("Dark").tag("dark")
            Text("System").tag("system")
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On iOS, the picker shows as a sheet. On macOS, it renders as a dropdown.&lt;/p&gt;
&lt;h2&gt;Date &amp;amp; Stepper Inputs&lt;/h2&gt;
&lt;p&gt;Need scheduling or number input? SwiftUI has it built-in.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;Form {
    Section("Event Details") {
        DatePicker("Date", selection: $eventDate, displayedComponents: .date)
        Stepper("Guests: \(guestCount)", value: $guestCount, in: 1...20)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is perfect for &lt;strong&gt;calendar apps, bookings, or reservations&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Styling with Section Footers&lt;/h2&gt;
&lt;p&gt;You can add &lt;strong&gt;footers&lt;/strong&gt; for extra context — useful for tips or validation hints.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;Form {
    Section(
        header: Text("Account"),
        footer: Text("Password must be at least 8 characters.")
    ) {
        SecureField("Password", text: $password)
        SecureField("Confirm Password", text: $confirmPassword)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The footer automatically adapts to the platform’s design.&lt;/p&gt;
&lt;h2&gt;Why use SwiftUI Forms?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Native look &amp;amp; feel across iOS, iPadOS, macOS, and watchOS&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Automatic layout adjustments&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Built-in components for most common inputs&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Lightweight and declarative&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui"&gt;https://developer.apple.com/documentation/swiftui&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/form"&gt;https://developer.apple.com/documentation/swiftui/form&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/section"&gt;https://developer.apple.com/documentation/swiftui/section&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/textfield"&gt;https://developer.apple.com/documentation/swiftui/textfield&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/securefield"&gt;https://developer.apple.com/documentation/swiftui/securefield&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/toggle"&gt;https://developer.apple.com/documentation/swiftui/toggle&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/stepper"&gt;https://developer.apple.com/documentation/swiftui/stepper&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/datepicker"&gt;https://developer.apple.com/documentation/swiftui/datepicker&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>How to Position Views in SwiftUI</title>    <link>https://wesleydegroot.nl/blog/how-to-position-views-in-swiftui</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/how-to-position-views-in-swiftui</guid>
    <pubDate>Fri, 17 Oct 2025 22:37:48 +0200</pubDate>
    <category>SwiftUI</category>
    <description>&lt;p&gt;Sometimes you want to position your views precisely within a SwiftUI layout.&lt;/p&gt;
&lt;h2&gt;How to place your view on a specific coordinate&lt;/h2&gt;
&lt;p&gt;You can use the &lt;a href="https://developer.apple.com/documentation/swiftui/view/position(x:y:)"&gt;&lt;code&gt;position&lt;/code&gt;&lt;/a&gt; modifier to place a view at a specific point within its parent.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import Swift

struct ContentView: View {
    var body: some View {
        Circle()
            .fill(Color.blue)
            .frame(width: 100, height: 100)
            .position(x: 100, y: 100)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;p&gt;Using the &lt;a href="https://developer.apple.com/documentation/swiftui/view/position(x:y:)"&gt;&lt;code&gt;position&lt;/code&gt;&lt;/a&gt; modifier can lead to unexpected layouts, especially with dynamic content. It's often better to use alignment guides or other layout tools provided by SwiftUI.&lt;/p&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;In this guide, we've covered how to position views in SwiftUI using the &lt;a href="https://developer.apple.com/documentation/swiftui/view/position(x:y:)"&gt;&lt;code&gt;position&lt;/code&gt;&lt;/a&gt; modifier. While it's a powerful tool, be mindful of its caveats and consider alternative layout strategies when necessary.&lt;/p&gt;
&lt;p&gt;Resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui"&gt;https://developer.apple.com/documentation/swiftui&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/position(x:y"&gt;https://developer.apple.com/documentation/swiftui/view/position(x:y&lt;/a&gt;:)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>It's a wrap (2025)</title>    <link>https://wesleydegroot.nl/blog/its-a-wrap-2025</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/its-a-wrap-2025</guid>
    <pubDate>Wed, 31 Dec 2025 10:44:04 +0100</pubDate>
    <category>Wrapped</category>
    <category>2025</category>
    <description>&lt;p&gt;In 2025 I have written 68 articles!&lt;/p&gt;
&lt;p&gt;The most used tags are: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;, &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;, &lt;a href="/blog/tag/SwiftPM" style="color: #cc6666"&gt;SwiftPM&lt;/a&gt;, &lt;a href="/blog/tag/macOS" style="color: #9900cc"&gt;macOS&lt;/a&gt;, &lt;a href="/blog/tag/iOS" style="color: #33cc66"&gt;iOS&lt;/a&gt;, &lt;/p&gt;
&lt;h3&gt;January&lt;/h3&gt;
&lt;p&gt;In January I've released 4 articles&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/apple-version-numbers"&gt;Apple version numbers&lt;/a&gt; (1 min) [Tags: &lt;a href="/blog/tag/Apple" style="color: #996699"&gt;Apple&lt;/a&gt;, &lt;a href="/blog/tag/version numbers" style="color: #ff6666"&gt;version numbers&lt;/a&gt;]&lt;br&gt;
As you may know, Apple releases new versions of its operating systems every year. The version numbers are not always the same across all platforms, and they don't always follow the same pattern. Her...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/accessibility-in-swiftui"&gt;Accessibility in SwiftUI&lt;/a&gt; (6 min) [Tags: &lt;a href="/blog/tag/Accessibility" style="color: #ccccff"&gt;Accessibility&lt;/a&gt;, &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;]&lt;br&gt;
Accessibility in app development is all about making sure everyone, regardless of their abilities, can use your app. Apple’s SwiftUI framework makes integrating accessibility features more streaml...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/contactprovider"&gt;Contact Provider Extension&lt;/a&gt; (8 min) [Tags: &lt;a href="/blog/tag/ContactProvider" style="color: #ff33cc"&gt;ContactProvider&lt;/a&gt;, &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;]&lt;br&gt;
The ContactProvider framework lets your app create (business) contacts on the user's device.   It is the prefered way to add contacts to the user's address book without interferring with their (perso...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/metrickit"&gt;Using MetricKit&lt;/a&gt; (2 min) [Tags: &lt;a href="/blog/tag/MetricKit" style="color: #993366"&gt;MetricKit&lt;/a&gt;, &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;]&lt;br&gt;
In this post, I will show you how to use MetricKit to collect and analyze performance data from your app.   MetricKit is a framework introduced in iOS 13 that allows you to collect and analyze perfor...&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;February&lt;/h3&gt;
&lt;p&gt;In February I've released 4 articles&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/app-states-in-ios"&gt;iOS App States&lt;/a&gt; (2 min) [Tags: &lt;a href="/blog/tag/iOS" style="color: #33cc66"&gt;iOS&lt;/a&gt;, &lt;a href="/blog/tag/App State" style="color: #ccff33"&gt;App State&lt;/a&gt;]&lt;br&gt;
When you're developing an iOS app, it's important to understand the different states that your app can be in, to handle them correctly and provide a smooth user experience. In this post, we'll take a...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/controlling-login-and-background-items"&gt;Controllig login and background items&lt;/a&gt; (3 min) [Tags: &lt;a href="/blog/tag/macOS" style="color: #9900cc"&gt;macOS&lt;/a&gt;, &lt;a href="/blog/tag/background" style="color: #cc33cc"&gt;background&lt;/a&gt;, &lt;a href="/blog/tag/login" style="color: #cc6666"&gt;login&lt;/a&gt;]&lt;br&gt;
In This post, we'll take a look at how to control login and background items on macOS. When you start your Mac, you may have noticed that some apps or items automatically start up or appear in the ba...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/controlgroup-in-swiftui"&gt;Exploring ControlGroup in SwiftUI&lt;/a&gt; (3 min) [Tags: &lt;a href="/blog/tag/ControlGroup" style="color: #333366"&gt;ControlGroup&lt;/a&gt;, &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;]&lt;br&gt;
In SwiftUI, a ControlGroup provides a way to visually group controls together, making your UI more intuitive and structured. It helps you present related controls in a unified manner, enhancing the ...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/expressible-literals"&gt;Expressible Literals&lt;/a&gt; (2 min) [Tags: &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;, &lt;a href="/blog/tag/expressible-literals" style="color: #ff00cc"&gt;expressible-literals&lt;/a&gt;]&lt;br&gt;
Expressible Literals in Swift are a powerful feature that allows you to create custom types that can be initialized using literals.   This post will show you how to create your own custom types that ...&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;March&lt;/h3&gt;
&lt;p&gt;In March I've released 6 articles&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/javascriptcore"&gt;JavaScriptCore&lt;/a&gt; (5 min) [Tags: &lt;a href="/blog/tag/JavaScriptCore" style="color: #6666cc"&gt;JavaScriptCore&lt;/a&gt;, &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;]&lt;br&gt;
Whether you love it or hate it, JavaScript has become the most important language for developers in the world today. Yet despite any efforts we may take to change or replace it we’d be hard  JavaSc...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/macos-terminal-commands"&gt;MacOS Terminal Commands&lt;/a&gt; (5 min) [Tags: &lt;a href="/blog/tag/macOS" style="color: #9900cc"&gt;macOS&lt;/a&gt;, &lt;a href="/blog/tag/Terminal" style="color: #666699"&gt;Terminal&lt;/a&gt;]&lt;br&gt;
In this post i'll show you my favorite macOS terminal commands that i use often or find interesting. This blog post is a different kind of post than i usually write, but i hope you like it.  | Descr...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/navigationstack"&gt;NavigationStack&lt;/a&gt; (4 min) [Tags: &lt;a href="/blog/tag/NavigationStack" style="color: #cc9933"&gt;NavigationStack&lt;/a&gt;, &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;]&lt;br&gt;
NavigationStack is a SwiftUI view that provides a navigation stack for macOS and iOS.   Navigation was done using NavigationView, but it was deprecated in iOS 16 and was split into two new containers...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/build-a-personal-brand-as-developer"&gt;Build a personal brand as developer&lt;/a&gt; (2 min) [Tags: &lt;a href="/blog/tag/Developer" style="color: #663399"&gt;Developer&lt;/a&gt;, &lt;a href="/blog/tag/Brand" style="color: #33ffff"&gt;Brand&lt;/a&gt;]&lt;br&gt;
Here are some tips to build a personal brand as a developer.   Start by defining your brand.   This will help you to create a consistent message and image.    Build a professional website to showcase...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/appdevcon-2025"&gt;AppDevCon 2025&lt;/a&gt; (4 min) [Tags: &lt;a href="/blog/tag/AppDevCon" style="color: #cc99cc"&gt;AppDevCon&lt;/a&gt;, &lt;a href="/blog/tag/Conference" style="color: #9966cc"&gt;Conference&lt;/a&gt;]&lt;br&gt;
AppDevCon is a conference for application developers, focusing on the latest trends and technologies in app development.  Below is a brief overview of some of the talks at the conference, with a smal...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/fixing-slow-scrolling-in-calendo"&gt;Fixing slow scrolling in Calendo&lt;/a&gt; (7 min) [Tags: &lt;a href="/blog/tag/Calendo" style="color: #999966"&gt;Calendo&lt;/a&gt;, &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;]&lt;br&gt;
In this post, I'll go through the steps which i took to fix a bug with slow scrolling in Calendo. Interested in the development of Calendo? Read my previous post about Building Calendo or search on t...&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;April&lt;/h3&gt;
&lt;p&gt;In April I've released 5 articles&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/dutch.swift"&gt;Dutch.swift&lt;/a&gt; (1 min) [Tags: &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;]&lt;br&gt;
For april fools day, I wanted to write a Swift library that allows you to write Swift code in Dutch. It's called Dutch.swift and it's available on GitHub. Unfortunately it didn't work out in the way...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/building-iwebtools"&gt;Building iWebTools&lt;/a&gt; (17 min) [Tags: &lt;a href="/blog/tag/iWebTools" style="color: #333366"&gt;iWebTools&lt;/a&gt;]&lt;br&gt;
In this blog post I will show you how I build iWebTools. iWebTools is a tool to check websites and use some tools like a JWT Decoder and a crontab converter.  I started with a basic design, I'm not r...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/numberformatter"&gt;NumberFormatter&lt;/a&gt; (3 min) [Tags: &lt;a href="/blog/tag/NumberFormatter" style="color: #996666"&gt;NumberFormatter&lt;/a&gt;, &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;]&lt;br&gt;
NumberFormatter is a class that provides a flexible and easy way to convert numbers into strings and vice versa.   It is a part of the Foundation framework and is available on all Apple platforms.   ...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/start-your-own-blog"&gt;Tips and ideas to start your own blog&lt;/a&gt; (5 min) [Tags: &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;, &lt;a href="/blog/tag/Blog" style="color: #cc99ff"&gt;Blog&lt;/a&gt;]&lt;br&gt;
When I started this website on 03 February 2024 I was unsure if I would be able to keep it up. I had tried to start a blog some times before but I always struggled to keep it up. This time I take an...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/what-powers-this-website"&gt;What powers this website&lt;/a&gt; (4 min) [Tags: &lt;a href="/blog/tag/Blog" style="color: #cc99ff"&gt;Blog&lt;/a&gt;, &lt;a href="/blog/tag/PHP" style="color: #33ff33"&gt;PHP&lt;/a&gt;]&lt;br&gt;
When I started this website on 03 February 2024, it was a small project, with one php file for rendering markdown files.  This website is made in Visual Studio Code, with plain HTML, CSS, and a litt...&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;May&lt;/h3&gt;
&lt;p&gt;In May I've released 4 articles&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/building-xcstrings-translator"&gt;Building xcstrings-translator&lt;/a&gt; (9 min) [Tags: &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;, &lt;a href="/blog/tag/xcstrings" style="color: #cc0033"&gt;xcstrings&lt;/a&gt;]&lt;br&gt;
xcstrings  The idea behind this tool is to provide a simple and easy way to translate .xcstrings files. The tool should be able to read the .xcstrings file, my original idea was to make this a comman...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/customizing-macos-windows"&gt;Customizing macOS windows&lt;/a&gt; (5 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;, &lt;a href="/blog/tag/macOS" style="color: #9900cc"&gt;macOS&lt;/a&gt;]&lt;br&gt;
MacOS windows are a fundamental part of the user interface, and customizing them can greatly enhance the user experience. In this post, we will explore how to customize macOS windows using SwiftUI. ...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/creating-macos-menu-bar-app"&gt;Creating macOS Menu Bar App in SwiftUI&lt;/a&gt; (3 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;, &lt;a href="/blog/tag/macOS" style="color: #9900cc"&gt;macOS&lt;/a&gt;, &lt;a href="/blog/tag/MenuBar" style="color: #996633"&gt;MenuBar&lt;/a&gt;]&lt;br&gt;
Creating a menu bar app may seems like a complicated task, but with SwiftUI, it can be done in a few simple steps. In this post, we will create a basic macOS menu bar app using SwiftUI. The app will...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/popovers-in-swiftui"&gt;Popovers in SwiftUI&lt;/a&gt; (3 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;]&lt;br&gt;
In SwiftUI, popovers are a great way to present additional information or options without navigating away from the current view. This post will explore how to create and customize popovers in SwiftUI...&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;June&lt;/h3&gt;
&lt;p&gt;In June I've released 4 articles&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/handle-plurals-in-swift-with-inflection"&gt;Handle plurals in Swift with inflection&lt;/a&gt; (2 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;]&lt;br&gt;
Inflection is a powerful tool in Swift that allows developers to handle plurals and other linguistic variations in a more elegant way. This post will explore how to use inflection in Swift, providing...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/cocoapods-trunk-read-only-plan"&gt;CocoaPods Trunk Read-only Plan&lt;/a&gt; (1 min) [Tags: &lt;a href="/blog/tag/CocoaPods" style="color: #99ff99"&gt;CocoaPods&lt;/a&gt;]&lt;br&gt;
CocoaPods Trunk Read  CocoaPods is a dependency manager for Swift and ObjectiveIt is used to manage library dependencies in iOS and macOS applications, allowing developers to easily integrate thirdIt...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/thermal-states-on-ios"&gt;Thermal States on iOS&lt;/a&gt; (3 min) [Tags: &lt;a href="/blog/tag/iOS" style="color: #33cc66"&gt;iOS&lt;/a&gt;, &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;]&lt;br&gt;
You can read the thermal states on iOS documentation to understand how to manage the thermal state of your iOS application. This is important for optimizing performance and battery life.   Thermal st...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/move-your-app-to-the-background"&gt;Move your app to the background&lt;/a&gt; (2 min) [Tags: &lt;a href="/blog/tag/iOS" style="color: #33cc66"&gt;iOS&lt;/a&gt;]&lt;br&gt;
In this (small) post, we will explore how to move your iOS application to the background programmatically. This is offcially not supported by Apple.   `swift import SwiftUI  struct ContentView: View ...&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;July&lt;/h3&gt;
&lt;p&gt;In July I've released 5 articles&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/using-the-share-sheet-to-share-content"&gt;Using the share sheet to share content&lt;/a&gt; (4 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;]&lt;br&gt;
In this post, we will explore how to use the share sheet in SwiftUI to share content from your app. The share sheet allows users to share text, images, URLs, and other types of content with other app...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/gradients-in-text-using-foregroundstyle"&gt;Gradients in text using foregroundStyle&lt;/a&gt; (2 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;]&lt;br&gt;
In this post, we will explore how to create gradients in text using the foregroundStyle modifier in SwiftUI. This technique allows for visually appealing text effects that can enhance the user interf...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/difference-between-animations-in-swiftui"&gt;Difference between animations in SwiftUI&lt;/a&gt; (6 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;, &lt;a href="/blog/tag/Animations" style="color: #003333"&gt;Animations&lt;/a&gt;]&lt;br&gt;
In this post, we will explore the difference between .animation()) and withAnimation()) and phaseAnimator()) in SwiftUI. Both are used to create animations, but they serve different purposes and have...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/adjust-the-intensity-of-colors-in-swiftui-views"&gt;Adjust the intensity of colors in SwiftUI views&lt;/a&gt; (3 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;, &lt;a href="/blog/tag/Colors" style="color: #666699"&gt;Colors&lt;/a&gt;, &lt;a href="/blog/tag/Brightness" style="color: #996633"&gt;Brightness&lt;/a&gt;]&lt;br&gt;
SwiftUI provides a powerful way to create dynamic and visually appealing user interfaces. One of the features that can enhance the visual experience is the ability to adjust the intensity of colors i...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/swiftui-buttons"&gt;SwiftUI Buttons&lt;/a&gt; (4 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;, &lt;a href="/blog/tag/Button" style="color: #99cc66"&gt;Button&lt;/a&gt;]&lt;br&gt;
In this post we will explore the different styles of buttons available in SwiftUI, how to create custom buttons, and some best practices for using buttons in your SwiftUI applications.   Buttons in S...&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;August&lt;/h3&gt;
&lt;p&gt;In August I've released 5 articles&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/surprise-route"&gt;Surprise Route&lt;/a&gt; (6 min) [Tags: &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;]&lt;br&gt;
In this post, I'll take you tough the process of creating my Surprise Route app in Swift(UI). This app is designed to generate random routes for walking or biking, making your daily exercise routine ...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/swiftui-lists"&gt;SwiftUI Lists&lt;/a&gt; (16 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;, &lt;a href="/blog/tag/List" style="color: #66cc99"&gt;List&lt;/a&gt;]&lt;br&gt;
Discover List in SwiftUI, a powerful way to display collections of data in a scrollable format. In this post, we will explore how to create lists, customize their appearance, and handle user interact...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/interpolation-and-formatting-in-text"&gt;Interpolation and formatting in Text&lt;/a&gt; (3 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;, &lt;a href="/blog/tag/Interpolation" style="color: #66ff00"&gt;Interpolation&lt;/a&gt;, &lt;a href="/blog/tag/Formatting" style="color: #666633"&gt;Formatting&lt;/a&gt;]&lt;br&gt;
In this post, we will explore how to use interpolation and formatting in SwiftUI's Text view. This is a powerful feature that allows you to create dynamic and formatted text easily.   Interpolation a...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/placing-components-within-the-safe-area-inset"&gt;Placing components within the Safe Area Inset&lt;/a&gt; (2 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;, &lt;a href="/blog/tag/SafeArea" style="color: #66cc33"&gt;SafeArea&lt;/a&gt;]&lt;br&gt;
In this post, we will explore how to effectively place components within the Safe Area Inset in SwiftUI applications.   The Safe Area Inset is a layout guide in iOS that defines the portion of the sc...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/building-editable-lists-in-swiftui"&gt;Building Editable Lists in SwiftUI&lt;/a&gt; (4 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;, &lt;a href="/blog/tag/List" style="color: #66cc99"&gt;List&lt;/a&gt;]&lt;br&gt;
In this post, we will explore how to create editable lists in SwiftUI using a couple of different approaches using EditButton(), @Environment(.editMode) and custom EditMode state.   List is a SwiftU...&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;September&lt;/h3&gt;
&lt;p&gt;In September I've released 5 articles&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/building-swiftui-debugging-utilities"&gt;Building SwiftUI Debugging Utilities&lt;/a&gt; (2 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;, &lt;a href="/blog/tag/Debug" style="color: #990099"&gt;Debug&lt;/a&gt;]&lt;br&gt;
In this post, we will explore how to create debugging utilities in SwiftUI to help diagnose and fix issues in your app.   As you may know already you cannot use print()) directly in your SwiftUI code...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/remove-the-background-from-images-using-swift"&gt;Remove the background from images using Swift&lt;/a&gt; (8 min) [Tags: &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;]&lt;br&gt;
Removing backgrounds from images is a common task in modern apps, whether for photo editing, social media, or automation. In this article, you'll learn how to build a cross     Background removal l...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/custom-tabbar-with-swiftui"&gt;Custom Tabbar with SwiftUI&lt;/a&gt; (3 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;, &lt;a href="/blog/tag/TabBar" style="color: #99cc66"&gt;TabBar&lt;/a&gt;]&lt;br&gt;
In this post, we will explore how to create a custom tab bar using SwiftUI.   A TabView is a user interface component that allows users to switch between different views or sections of an app. Unlike...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/hidden-macos-features"&gt;Hidden macOS features&lt;/a&gt; (2 min) [Tags: &lt;a href="/blog/tag/macOS" style="color: #9900cc"&gt;macOS&lt;/a&gt;]&lt;br&gt;
In this post we're going to explore some hidden features of macOS that can enhance your productivity and improve your workflow.   To move a file in finder you can use ⌘C (Command + C) to copy the f...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/sensory-feedback-in-swiftui"&gt;Sensory feedback in SwiftUI&lt;/a&gt; (3 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;, &lt;a href="/blog/tag/Trigger" style="color: #ff99ff"&gt;Trigger&lt;/a&gt;]&lt;br&gt;
This post explores the concept of sensory feedback in SwiftUI, a modifier to provide feedback to users based on their interactions.   Sensory Feedback in SwiftUI is a powerful feature that allows dev...&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;October&lt;/h3&gt;
&lt;p&gt;In October I've released 16 articles&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/hacktoberfest-2025"&gt;Hacktoberfest 2025&lt;/a&gt; (2 min) [Tags: &lt;a href="/blog/tag/Hacktoberfest" style="color: #996633"&gt;Hacktoberfest&lt;/a&gt;]&lt;br&gt;
As the leaves change color and the air turns crisp, developers around the world gear up for one of the most exciting events in the openThis annual celebration, organized by DigitalOcean, GitHub, and...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/viewthatfits"&gt;ViewThatFits&lt;/a&gt; (3 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;, &lt;a href="/blog/tag/ViewThatFits" style="color: #669900"&gt;ViewThatFits&lt;/a&gt;]&lt;br&gt;
SwiftUI is all about declarative UI and adaptive design. But sometimes, you need your view to intelligently choose between multiple layout options depending on the available space. That's where ViewT...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/swift-package-imagepicker"&gt;Swift Package: ImagePicker&lt;/a&gt; (1 min) [Tags: &lt;a href="/blog/tag/SwiftPM" style="color: #cc6666"&gt;SwiftPM&lt;/a&gt;, &lt;a href="/blog/tag/ImagePicker" style="color: #660099"&gt;ImagePicker&lt;/a&gt;]&lt;br&gt;
In this post, we will explore the ImagePicker Swift Package, a powerful tool for selecting images in your applications.   ImagePicker is a Swift Package that simplifies the process of selecting image...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/contextmenu"&gt;Contextmenu&lt;/a&gt; (2 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;, &lt;a href="/blog/tag/Contextmenu" style="color: #66cc33"&gt;Contextmenu&lt;/a&gt;]&lt;br&gt;
SwiftUI continues to surprise us with its elegant solutions to common UI patterns. One such gem is the ContextMenu)—a powerful modifier that lets you attach contextual actions to any view. Think of...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/swift-package-filepicker"&gt;Swift Package: FilePicker&lt;/a&gt; (2 min) [Tags: &lt;a href="/blog/tag/SwiftPM" style="color: #cc6666"&gt;SwiftPM&lt;/a&gt;, &lt;a href="/blog/tag/FilePicker" style="color: #33ff66"&gt;FilePicker&lt;/a&gt;]&lt;br&gt;
FilePicker is a Swift Package to open and save files in SwiftUI. It provides a simple way to open and save files in SwiftUI views.    To install FilePicker, add it to your Package.swift file:  `swift...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/swift-package-icloudstorage"&gt;Swift Package: iCloudStorage&lt;/a&gt; (1 min) [Tags: &lt;a href="/blog/tag/SwiftPM" style="color: #cc6666"&gt;SwiftPM&lt;/a&gt;, &lt;a href="/blog/tag/iCloudStorage" style="color: #99cc66"&gt;iCloudStorage&lt;/a&gt;]&lt;br&gt;
In this post, we will explore the iCloudStorage Swift Package, a powerful tool for managing iCloud key  iCloudStorage is a Swift Package that simplifies the process of storing and retrieving key  To ...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/swift-package-securestorage"&gt;Swift Package: SecureStorage&lt;/a&gt; (2 min) [Tags: &lt;a href="/blog/tag/SwiftPM" style="color: #cc6666"&gt;SwiftPM&lt;/a&gt;, &lt;a href="/blog/tag/SecureStorage" style="color: #cc33cc"&gt;SecureStorage&lt;/a&gt;]&lt;br&gt;
In this post, we will explore the SecureStorage Swift Package, a powerful tool for managing sensitive data in your applications.   SecureStorage is a Swift Package that simplifies the process of stor...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/supporting-reduced-motion-accessibility-setting-in-swiftui"&gt;Supporting Reduced Motion accessibility setting in SwiftUI&lt;/a&gt; (2 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;, &lt;a href="/blog/tag/Accessibility" style="color: #ccccff"&gt;Accessibility&lt;/a&gt;]&lt;br&gt;
In this post, we will explore how to support the Reduced Motion accessibility setting in SwiftUI applications.   The Reduced Motion accessibility setting in iOS allows users to minimize the amount of...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/swift-package-xcuitesthelper"&gt;Swift Package: XCUITestHelper&lt;/a&gt; (2 min) [Tags: &lt;a href="/blog/tag/SwiftPM" style="color: #cc6666"&gt;SwiftPM&lt;/a&gt;, &lt;a href="/blog/tag/XCUITestHelper" style="color: #333399"&gt;XCUITestHelper&lt;/a&gt;]&lt;br&gt;
In this post, we will explore the XCUITestHelper Swift Package, a powerful tool for simplifying UI testing in your applications.   XCUITestHelper is a Swift Package that provides a set of utilities a...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/swift-package-preventscreenshot"&gt;Swift Package: PreventScreenshot&lt;/a&gt; (2 min) [Tags: &lt;a href="/blog/tag/SwiftPM" style="color: #cc6666"&gt;SwiftPM&lt;/a&gt;, &lt;a href="/blog/tag/PreventScreenshot" style="color: #0066cc"&gt;PreventScreenshot&lt;/a&gt;]&lt;br&gt;
In this post, we will explore a Swift package designed to prevent screenshots in iOS applications.   PreventScreenshot is a Swift package that provides a simple way to prevent users from taking scree...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/swift-package-networkmonitor"&gt;Swift Package: NetworkMonitor&lt;/a&gt; (3 min) [Tags: &lt;a href="/blog/tag/SwiftPM" style="color: #cc6666"&gt;SwiftPM&lt;/a&gt;, &lt;a href="/blog/tag/NetworkMonitor" style="color: #33cc66"&gt;NetworkMonitor&lt;/a&gt;]&lt;br&gt;
In this post, we will explore a Swift package designed to monitor network connectivity in iOS applications.   NetworkMonitor is a Swift package that provides a simple way to monitor the network conne...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/post-identifiable-protocol"&gt;Identifiable protocol&lt;/a&gt; (3 min) [Tags: &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;, &lt;a href="/blog/tag/Identifiable" style="color: #ccff99"&gt;Identifiable&lt;/a&gt;]&lt;br&gt;
Identifiable is a protocol in Swift that allows you to uniquely identify instances of a type.   The Identifiable protocol is a protocol in Swift that allows you to uniquely identify instances of a ty...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/swift-package-swiftcronparser"&gt;Swift Package: SwiftCronParser&lt;/a&gt; (1 min) [Tags: &lt;a href="/blog/tag/SwiftPM" style="color: #cc6666"&gt;SwiftPM&lt;/a&gt;, &lt;a href="/blog/tag/SwiftCronParser" style="color: #993399"&gt;SwiftCronParser&lt;/a&gt;]&lt;br&gt;
In this post, we will explore the SwiftCronParser Swift Package, a package to parse cron expressions in Swift.   SwiftCronParser is a Swift package that provides a simple and efficient way to parse c...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/swift-package-swiftextras"&gt;Swift Package: SwiftExtras&lt;/a&gt; (2 min) [Tags: &lt;a href="/blog/tag/SwiftPM" style="color: #cc6666"&gt;SwiftPM&lt;/a&gt;, &lt;a href="/blog/tag/SwiftExtras" style="color: #000099"&gt;SwiftExtras&lt;/a&gt;]&lt;br&gt;
In this post, we will explore the SwiftExtras Swift Package, a package that provides additional functionality and utilities for Swift developers.   SwiftExtras is a Swift package that offers a collec...&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;November&lt;/h3&gt;
&lt;p&gt;In November I've released 4 articles&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/quick-actions-for-swiftui"&gt;Quick actions in SwiftUI&lt;/a&gt; (4 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;, &lt;a href="/blog/tag/QuickActions" style="color: #ccccff"&gt;QuickActions&lt;/a&gt;]&lt;br&gt;
Quick actions are a powerful feature in SwiftUI that allows developers to add contextually relevant actions to their views. These actions can be triggered by user interactions, such as tapping and ho...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/swiftui-picker"&gt;Picker in SwiftUI&lt;/a&gt; (4 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;, &lt;a href="/blog/tag/Picker" style="color: #ff3399"&gt;Picker&lt;/a&gt;]&lt;br&gt;
The Picker is a SwiftUI view that presents a set of options for the user to choose from. It can be displayed as a dropdown menu, a segmented control, or a wheel, depending on the context and the numb...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/monospace-digits"&gt;Monospace digits&lt;/a&gt; (2 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;, &lt;a href="/blog/tag/Monospace" style="color: #99ff33"&gt;Monospace&lt;/a&gt;]&lt;br&gt;
Monospace digits are a type of font where each character takes up the same amount of horizontal space. This is particularly useful in programming and data presentation, as it allows for better alignm...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/environment-openurl"&gt;Environment: OpenURL&lt;/a&gt; (1 min) [Tags: &lt;a href="/blog/tag/Environment" style="color: #009999"&gt;Environment&lt;/a&gt;, &lt;a href="/blog/tag/OpenURL" style="color: #cccc99"&gt;OpenURL&lt;/a&gt;]&lt;br&gt;
@Environment(.openURL) allows you to open URLs from your SwiftUI views.   @Environment(.openURL) is a property wrapper that provides a way to open URLs in your SwiftUI application. It gives you acc...&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;December&lt;/h3&gt;
&lt;p&gt;In December I've released 6 articles&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/inalternativeappnames"&gt;Make your app visible with alternative app names&lt;/a&gt; (1 min) [Tags: &lt;a href="/blog/tag/INAlternativeAppNames" style="color: #333300"&gt;INAlternativeAppNames&lt;/a&gt;]&lt;br&gt;
With INAlternativeAppNames, you can provide alternative names for your app so that the user can find your app more easily.   You can provide alternative names like Deposit, Safe, Bank, or Finance to ...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/tracking-screen-views-in-swiftui-with-a-custom-viewmodifier"&gt;Tracking Screen Views in SwiftUI with a Custom ViewModifier&lt;/a&gt; (2 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;, &lt;a href="/blog/tag/ViewModifier" style="color: #66ff66"&gt;ViewModifier&lt;/a&gt;, &lt;a href="/blog/tag/Analytics" style="color: #9966cc"&gt;Analytics&lt;/a&gt;]&lt;br&gt;
In this post we'll create a small extension to track which screens are being viewed in a SwiftUI application.   Tracking screen views is essential for understanding user behavior within your app. By ...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/task-sleep-vs-task-yield-the-differences-explained"&gt;Task.sleep() vs. Task.yield(): The differences explained&lt;/a&gt; (2 min) [Tags: &lt;a href="/blog/tag/Swift" style="color: #999933"&gt;Swift&lt;/a&gt;, &lt;a href="/blog/tag/Task" style="color: #ffff33"&gt;Task&lt;/a&gt;]&lt;br&gt;
Task.sleep() vs. Task.yield() The differences explained   With Task.sleep()), you can suspend the execution of a task for a specified duration. This is useful when you want to introduce a delay in yo...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/swiftui-form"&gt;Form in SwiftUI&lt;/a&gt; (2 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;, &lt;a href="/blog/tag/Form" style="color: #cc66cc"&gt;Form&lt;/a&gt;]&lt;br&gt;
User input powers almost every app. From reminders to events, contacts, or sign  The simplest way to start is by grouping TextField inside a Form. SwiftUI automatically handles grouping, scrolling, a...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/how-to-position-views-in-swiftui"&gt;How to Position Views in SwiftUI&lt;/a&gt; (1 min) [Tags: &lt;a href="/blog/tag/SwiftUI" style="color: #669966"&gt;SwiftUI&lt;/a&gt;]&lt;br&gt;
Sometimes you want to position your views precisely within a SwiftUI layout.   You can use the position) modifier to place a view at a specific point within its parent.  `swift import Swift  struct C...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/its-a-wrap-2025"&gt;It's a wrap (2025)&lt;/a&gt; (1 min) [Tags: &lt;a href="/blog/tag/Wrapped" style="color: #9999ff"&gt;Wrapped&lt;/a&gt;, &lt;a href="/blog/tag/2025" style="color: #333366"&gt;2025&lt;/a&gt;]&lt;br&gt;
In 2025 I have written 68 articles!  The most used tags are: &amp;lt;a href="/blog/tag/SwiftUI" style="color:   In January I've released 4 articles      In February I've released 4 articles      In March ...&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Reading minutes per month&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;January: 17 minutes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;February: 10 minutes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;March: 27 minutes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;April: 30 minutes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;May: 20 minutes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;June: 8 minutes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;July: 19 minutes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;August: 31 minutes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;September: 18 minutes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;October: 28 minutes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;November: 11 minutes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;December: 9 minutes&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The total reading time of all articles is about 228 minutes (3.8 hours)!&lt;/p&gt;</description>
  </item>
  <item>
    <title>Welcome 2026</title>    <link>https://wesleydegroot.nl/blog/welcome-2026</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/welcome-2026</guid>
    <pubDate>Mon, 13 Apr 2026 16:32:43 +0200</pubDate>
    <category>Website</category>
    <description>&lt;p&gt;Welcome to 2026!&lt;/p&gt;
&lt;p&gt;I hope you'll all have a fantastic year and that all your wishes may become true.&lt;/p&gt;
&lt;p&gt;We're going to start this year with &lt;strong&gt;&lt;a href="/blog/tag/Accessibility"&gt;accessibility&lt;/a&gt;&lt;/strong&gt; in mind. Stay tuned for more updates and exciting content throughout the year.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Topic&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;06-JAN-2026&lt;/td&gt;
&lt;td&gt;Dark Mode&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13-JAN-2026&lt;/td&gt;
&lt;td&gt;Reduced Motion (plus prefers-reduced-motion CSS media query for your websites!)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20-JAN-2026&lt;/td&gt;
&lt;td&gt;Sufficient Contrast&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;27-JAN-2026&lt;/td&gt;
&lt;td&gt;Larger Text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;03-FEB-2026&lt;/td&gt;
&lt;td&gt;Differentiate without color&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10-FEB-2026&lt;/td&gt;
&lt;td&gt;VoiceOver&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17-FEB-2026&lt;/td&gt;
&lt;td&gt;Voice Control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;24-FEB-2026&lt;/td&gt;
&lt;td&gt;Captions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;03-MAR-2026&lt;/td&gt;
&lt;td&gt;Audio Descriptions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;I'm excited to share more with you all!&lt;/p&gt;
&lt;p&gt;I would love to hear your thoughts and suggestions. Please feel free to reach out or leave a comment below!&lt;/p&gt;
&lt;p&gt;In addition to those important topics, I want to explore and learn about &lt;a href="https://developer.apple.com/game-center/"&gt;Game Center&lt;/a&gt;, &lt;a href="https://developer.apple.com/healthkit/"&gt;HealthKit&lt;/a&gt;, and other underused frameworks and technologies.&lt;/p&gt;
&lt;p&gt;I must make some hurry for writing the blogs, usually I'm about 2-3 months ahead, but I didn't had the energy to do so in the last monts of 2025, I already did one of the most diffucult things, setting subjects for the blogs.&lt;/p&gt;
&lt;p&gt;As of writing this (01-JAN-2026), I have planned blog posts until March 2026, so stay tuned for more exciting content!&lt;/p&gt;
&lt;h3&gt;Some personal updates from 2025:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;I got diagnosed with Diabetes type 2, this was devastating news, but I'm doing everything I can to live a healthier life, I exercise 7 days a week now and I'm on a strict diet. (I may quit the diabetes medication soon if my blood glucose levels stay good!)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I'm finally on a healthy weight (92 kg / 203 lbs), I lost 25+ kg (55 lbs) in 2025. (in total it's about 65 kg / 143 lbs since 2019).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I have a &lt;a href="https://www.facebook.com/0xWDG/posts/pfbid02kKJhaKCLD75TeoHBEmmRRBa5AMia9MTHCZS3GpmTqYoS7iPWMWkdn3UNzxT3mWehl"&gt;girlfriend (Tanya)&lt;/a&gt;, we are very happy!&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Let's make 2026 a great year together!&lt;/h3&gt;
&lt;p&gt;- Wesley de Groot.&lt;/p&gt;</description>
  </item>
  <item>
    <title>Dark Mode</title>    <link>https://wesleydegroot.nl/blog/dark-mode</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/dark-mode</guid>
    <pubDate>Mon, 13 Apr 2026 16:32:43 +0200</pubDate>
    <category>Accessibility</category>
    <category>SwiftUI</category>
    <description>&lt;p&gt;Dark Mode is an important feature that many users appreciate for its aesthetic appeal, and for people with visual impairments.&lt;/p&gt;
&lt;h2&gt;When do we need Dark Mode support?&lt;/h2&gt;
&lt;p&gt;Dark Mode support is essential.&lt;br&gt;
supporting Dark Mode can enhance accessibility for users with visual impairments who may find it easier to read content on a dark background.&lt;/p&gt;
&lt;h2&gt;How to implement Dark Mode in SwiftUI&lt;/h2&gt;
&lt;p&gt;Implementing Dark Mode in SwiftUI is straightforward. SwiftUI mostly adapts automatically to the system's appearance settings, you may use a theming system to use dark and light colors without reading the environment variable .colorScheme all the time.&lt;/p&gt;
&lt;h3&gt;Automatic Support&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            Text("SwiftUI Will Handle Dark Mode Automatically")
                .foregroundColor(.primary)
                .background(.background)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Custom View&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct ThemedView: View {
    @Environment(\.colorScheme)
    var colorScheme

    var foregroundColor: Color {
        colorScheme == .dark ? .black : .white
    }

    var backgroundColor: Color {
        colorScheme == .dark ? .white : .black
    }

    var body: some View {
        Text("This view adapts to Dark Mode, using custom colors!")
            .foregroundColor(foregroundColor)
            .background(backgroundColor)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Use Assets Catalog&lt;/h3&gt;
&lt;p&gt;You can define colors in your Assets catalog that automatically adapt to Light and Dark modes. When you create a new color set, you can specify different colors for "Any Appearance" and "Dark Appearance".&lt;/p&gt;
&lt;h3&gt;Forcing interface style&lt;/h3&gt;
&lt;p&gt;If you want to force a specific interface style for a view or the entire app, you can use the &lt;a href="https://developer.apple.com/documentation/swiftui/view/preferredcolorscheme(_:)"&gt;&lt;code&gt;.preferredColorScheme(_:)&lt;/code&gt;&lt;/a&gt; modifier:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct ForcedDarkModeView: View {
    var body: some View {
        VStack {
            Text("This view is always in Dark Mode")
        }
        .preferredColorScheme(.dark)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Libraries and Tools:&lt;/h2&gt;
&lt;p&gt;My package &lt;a href="https://github.com/0xWDG/Colors"&gt;Colors&lt;/a&gt; provides a convenient way to manage colors in your SwiftUI projects, including support for Dark Mode. You can define colors that automatically adjust based on the current appearance.&lt;/p&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;SwiftUI handles most of Dark Mode automatically — using semantic colors and &lt;code&gt;.preferredColorScheme(_:)&lt;/code&gt; is all most apps need. Read the &lt;code&gt;\.colorScheme&lt;/code&gt; environment value only when you genuinely need to branch behavior.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/0xWDG/Colors"&gt;https://github.com/0xWDG/Colors&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/colorscheme(_:)"&gt;colorScheme(_:)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/preferredcolorscheme(_:)"&gt;preferredColorScheme(_:)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/help/app-store-connect/manage-app-accessibility/dark-interface-evaluation-criteria/"&gt;https://developer.apple.com/help/app-store-connect/manage-app-accessibility/dark-interface-evaluation-criteria/&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Reduced Motion</title>    <link>https://wesleydegroot.nl/blog/reduced-motion</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/reduced-motion</guid>
    <pubDate>Mon, 30 Mar 2026 16:57:33 +0200</pubDate>
    <category>Accessibility</category>
    <category>SwiftUI</category>
    <category>Animations</category>
    <description>&lt;p&gt;Reduced motion is an important accessibility feature that helps create a comfortable user experience for individuals sensitive to motion effects.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;Reduced Motion&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Reduced Motion&lt;/code&gt; is an accessibility feature available on iOS, macOS, and other Apple platforms that minimizes the amount of motion and animation in the user interface. This feature is designed to help users who may experience motion sickness or discomfort from excessive animations and transitions.&lt;/p&gt;
&lt;p&gt;When &lt;code&gt;Reduced Motion&lt;/code&gt; is enabled, the system reduces or eliminates certain animations, such as parallax effects, screen transitions, and other dynamic visual effects. Developers can also respect this setting in their apps by checking the user's preference and adjusting their animations accordingly.&lt;/p&gt;
&lt;h2&gt;How to Check for Reduced Motion in SwiftUI&lt;/h2&gt;
&lt;p&gt;In SwiftUI, you can check if the user has enabled &lt;code&gt;Reduced Motion&lt;/code&gt; by using the &lt;code&gt;accessibilityReduceMotion&lt;/code&gt; environment value. You can then conditionally apply animations based on this setting.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ContentView: View {
    @Environment(\.accessibilityReduceMotion) var reduceMotion
    @State private var isVisible = false

    var body: some View {
        VStack {
            Button("Toggle Text") {
                if reduceMotion {
                    isVisible.toggle()
                } else {
                    withAnimation {
                        isVisible.toggle()
                    }
                }
            }

            if isVisible {
                Text("Hello, SwiftUI!")
                    .transition(.slide)
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Bonus: CSS&lt;/h2&gt;
&lt;p&gt;You can also reduce animations on your website (if you have any).&lt;br&gt;
You can copy &amp;amp; paste the following code in your css.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-css"&gt;/* Reduce motion support */
@media (prefers-reduced-motion: reduce) {
    *,
    ::before,
    ::after {
        animation-delay: -1ms !important;
        animation-duration: 1ms !important;
        animation-iteration-count: 1 !important;
        background-attachment: initial !important;
        scroll-behavior: auto !important;
        transition-duration: 0s !important;
        transition-delay: 0s !important;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;source: &lt;a href="https://web.dev/articles/prefers-reduced-motion#bonus_forcing_reduced_motion_on_all_websites"&gt;https://web.dev/articles/prefers-reduced-motion#bonus_forcing_reduced_motion_on_all_websites&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;In this post we've explored &lt;code&gt;Reduced Motion&lt;/code&gt; and how to respect user preferences in SwiftUI. By checking the &lt;code&gt;accessibilityReduceMotion&lt;/code&gt; environment value, you can ensure a comfortable experience for users who are sensitive to motion.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/tag/Accessibility"&gt;More posts about Accessibility&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/accessibility-fundamentals"&gt;Accessibility Fundamentals&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/SwiftUI/View-Accessibility"&gt;View Accessibility&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/environmentvalues"&gt;SwiftUI Environment Values&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Sufficient Contrast</title>    <link>https://wesleydegroot.nl/blog/sufficient-contrast</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/sufficient-contrast</guid>
    <pubDate>Mon, 30 Mar 2026 16:57:33 +0200</pubDate>
    <category>Accessibility</category>
    <category>SwiftUI</category>
    <description>&lt;p&gt;Sufficient contrast makes your text and UI elements readable for everyone — especially users with low vision or color blindness. WCAG recommends a minimum ratio of 4.5:1 for normal text and 3:1 for large text.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;Sufficient Contrast&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;Sufficient contrast refers to the difference in luminance or color that makes an object distinguishable from other objects and the background. In the context of user interfaces, it ensures that text and interactive elements are easily readable and accessible to all users, including those with visual impairments.&lt;/p&gt;
&lt;h2&gt;Why is Sufficient Contrast Important?&lt;/h2&gt;
&lt;p&gt;Sufficient contrast is vital for accessibility as it ensures that users with visual impairments, such as color blindness or low vision, can read and interact with content effectively. Poor contrast can lead to eye strain and make it difficult for users to navigate and understand information, ultimately excluding them from using your application.&lt;/p&gt;
&lt;h2&gt;How to Measure Contrast&lt;/h2&gt;
&lt;p&gt;Contrast is typically measured using a contrast ratio, which compares the luminance of the foreground (text or element) to the background. The Web Content Accessibility Guidelines (WCAG) recommend a minimum contrast ratio of 4.5:1 for normal text and 3:1 for large text.&lt;/p&gt;
&lt;p&gt;Pro tip: My app &lt;a href="/apps/iwebtools"&gt;iWebTools&lt;/a&gt; has a built-in contrast checker that you can use to evaluate the contrast ratios of your color choices.&lt;/p&gt;
&lt;h2&gt;Contrast checker&lt;/h2&gt;
&lt;!-- If you copy-paste the following code, please mention where you got it from. --&gt;
&lt;!-- Source: https://wesleydegroot.nl/blog/sufficient-contrast | https://wesleydegroot.nl/contrast-tool --&gt;
&lt;table class="plain"&gt;&lt;tr&gt;&lt;td&gt;&lt;label for="foreground-color"&gt;Foreground Color&lt;/label&gt;&lt;input type="color" id="foreground-color" name="foreground-color" value="#0000FF"&gt;&lt;/td&gt;&lt;td&gt;&lt;label for="background-color"&gt;Background Color&lt;/label&gt;&lt;input type="color" id="background-color" name="background-color" value="#ffffff"&gt;&lt;/td&gt;&lt;td style="text-align:center"&gt;Ratio&lt;br&gt;&lt;span id="contrast-ratio"&gt;&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;div id="results"&gt;&lt;strong&gt;Normal Text&lt;/strong&gt;&amp;nbsp;&lt;small&gt;Minimum contrast ratio of 4.5:1&lt;/small&gt;&lt;table class="plain"&gt;&lt;tr&gt;&lt;td&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;WCAG AA&lt;/td&gt;&lt;td id="result_text_normal_aa"&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;WCAG AAA&lt;/td&gt;&lt;td id="result_text_normal_aaa"&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/td&gt;&lt;td&gt;&lt;span id="result_text_normal"&gt;Normal Text Example&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;strong&gt;Large Text&lt;/strong&gt;&amp;nbsp;&lt;small&gt;Minimum contrast ratio of 3:1&lt;/small&gt;&lt;table class="plain"&gt;&lt;tr&gt;&lt;td&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;WCAG AA&lt;/td&gt;&lt;td id="result_text_large_aa"&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;WCAG AAA&lt;/td&gt;&lt;td id="result_text_large_aaa"&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/td&gt;&lt;td&gt;&lt;span id="result_text_large"&gt;Large Text Example&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;strong&gt;Graphical Objects and User Interface Components&lt;/strong&gt;&amp;nbsp;&lt;small&gt;Minimum contrast ratio of 3:1&lt;/small&gt;&lt;table class="plain"&gt;&lt;tr&gt;&lt;td&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;WCAG AA&lt;/td&gt;&lt;td id="result_ui_aa"&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;WCAG AAA&lt;/td&gt;&lt;td id="result_ui_aaa"&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/td&gt;&lt;td&gt;&lt;span id="result_ui"&gt;★ &lt;input id="result_ui_checkbox" type="checkbox" checked&gt; UI Component Example&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;script&gt;updateResults();document.getElementById('foreground-color').addEventListener('input',updateResults);document.getElementById('background-color').addEventListener('input',updateResults);function $el(selector){return document.querySelector(selector);}function updateResults() {const fgColor=document.getElementById('foreground-color').value;const bgColor=document.getElementById('background-color').value;const contrastRatio=getContrastRatio(fgColor, bgColor);$el('#result_text_normal').style.color=fgColor;$el('#result_text_normal').style.backgroundColor=bgColor;$el('#result_text_normal_aa').innerText=contrastRatio&gt;=4.5?'PASS':'FAIL';$el('#result_text_normal_aaa').innerText=contrastRatio&gt;=7?'PASS':'FAIL';$el('#result_text_large').style.color=fgColor;$el('#result_text_large').style.backgroundColor=bgColor;$el('#result_text_large_aa').innerText=contrastRatio&gt;=3?'PASS':'FAIL';$el('#result_text_large_aaa').innerText=contrastRatio&gt;=4.5?'PASS':'FAIL';$el('#result_ui').style.color=fgColor;$el('#result_ui').style.backgroundColor=bgColor;$el('#result_ui_checkbox').style.accentColor=fgColor;$el('#result_ui_aa').innerText=contrastRatio&gt;=3?'PASS':'FAIL';$el('#result_ui_aaa').innerText=contrastRatio&gt;=4.5?'PASS':'FAIL';$el('#contrast-ratio').innerText=contrastRatio.toFixed(2)+':1';}function getLuminance(hex) {const rgb=hexToRgb(hex);const a=[rgb.r, rgb.g, rgb.b].map(function (value){value/=255;return value &lt;= 0.03928?value/12.92:Math.pow((value+0.055)/1.055, 2.4)});return 0.2126 * a[0]+0.7152 * a[1]+0.0722 * a[2]}function getContrastRatio(fgHex, bgHex) {const L1=getLuminance(fgHex);const L2=getLuminance(bgHex);return (Math.max(L1, L2)+0.05)/(Math.min(L1, L2)+0.05)}function hexToRgb(hex){const bigint=parseInt(hex.slice(1),16);return{r:(bigint&gt;&gt;16)&amp;255,g:(bigint&gt;&gt;8)&amp;255,b:bigint&amp;255}}&lt;/script&gt;&lt;style&gt;input[type="color"]{cursor:pointer;display:block;height:34px;padding:0;width:206px;}#result_text_large,#contrast-ratio{font-size:1.5em;font-weight:bold;}&lt;/style&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;Getting contrast right is one of the more impactful accessibility improvements you can make — and it benefits everyone, not just users with visual impairments.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/tag/Accessibility"&gt;More posts about Accessibility&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/accessibility-fundamentals"&gt;Accessibility Fundamentals&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/help/app-store-connect/manage-app-accessibility/sufficient-contrast-evaluation-criteria"&gt;Sufficient contrast evaluation criteria&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Larger Text</title>    <link>https://wesleydegroot.nl/blog/larger-text</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/larger-text</guid>
    <pubDate>Mon, 30 Mar 2026 16:57:34 +0200</pubDate>
    <category>Accessibility</category>
    <category>SwiftUI</category>
    <description>&lt;p&gt;iOS lets users set their preferred text size system-wide through Accessibility settings. SwiftUI's dynamic type support handles most of this automatically — as long as you use semantic font styles like &lt;code&gt;.body&lt;/code&gt; or &lt;code&gt;.headline&lt;/code&gt; rather than fixed sizes.&lt;/p&gt;
&lt;h2&gt;Why Larger Text?&lt;/h2&gt;
&lt;p&gt;Larger text improves readability and accessibility for users with visual impairments or those who prefer bigger fonts for comfort. It ensures that content is easily legible, reducing eye strain and enhancing the overall user experience.&lt;/p&gt;
&lt;h2&gt;Implementation in SwiftUI&lt;/h2&gt;
&lt;p&gt;SwiftUI provides built-in support for dynamic type if the user has enabled larger text sizes in their device settings. To make your SwiftUI app support larger text, you can use the &lt;a href="https://developer.apple.com/documentation/swiftui/view/font(_:)"&gt;&lt;code&gt;.font&lt;/code&gt;&lt;/a&gt; modifier with dynamic type styles.&lt;/p&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;p&gt;Larger text can lead to layout issues if not handled properly. Ensure that your UI components can adapt to different text sizes without clipping or overlapping. Use flexible layouts and consider testing with various text sizes.&lt;/p&gt;
&lt;p&gt;You can use &lt;a href="/blog/viewthatfits"&gt;&lt;code&gt;ViewThatFits&lt;/code&gt;&lt;/a&gt; to create views that adapt to different text sizes gracefully.&lt;br&gt;
You can also use the &lt;a href="https://developer.apple.com/documentation/swiftui/environmentvalues/minimumscalefactor"&gt;&lt;code&gt;.minimumScaleFactor(_:)&lt;/code&gt;&lt;/a&gt; modifier to allow text to shrink if necessary, ensuring it fits within its bounds.&lt;/p&gt;
&lt;h2&gt;Testing Larger Text&lt;/h2&gt;
&lt;p&gt;To test larger text support, you can &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Use the Accessibility Inspector in Xcode to simulate different text sizes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Change the text size in the Accessibility settings on your device or simulator. Navigate to Settings &amp;gt; Accessibility &amp;gt; Display &amp;amp; Text Size &amp;gt; Larger Text, and adjust the slider to see how your app responds to different text sizes. &lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;View Example&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ContentView: View {
    var body: some View {
        ViewThatFits {
            Text("This is an example of larger text support in SwiftUI.")
                .font(.largeTitle) // Use a dynamic type style
                .padding()

            Text("This is an example of larger text support in SwiftUI.")
                .font(.title) // Fallback for smaller sizes
                .padding()
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;Supporting dynamic type mostly means choosing the right font styles and testing with larger sizes. &lt;a href="/blog/viewthatfits"&gt;&lt;code&gt;ViewThatFits&lt;/code&gt;&lt;/a&gt; and &lt;code&gt;.minimumScaleFactor(_:)&lt;/code&gt; are useful when layouts get tight.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/tag/Accessibility"&gt;More posts about Accessibility&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/accessibility-fundamentals"&gt;Accessibility Fundamentals&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/help/app-store-connect/manage-app-accessibility/larger-text-evaluation-criteria"&gt;Larger Text Evaluation Criteria&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Differentiate Without Color</title>    <link>https://wesleydegroot.nl/blog/differentiate-without-color</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/differentiate-without-color</guid>
    <pubDate>Mon, 30 Mar 2026 16:57:34 +0200</pubDate>
    <category>Accessibility</category>
    <category>SwiftUI</category>
    <description>&lt;p&gt;Color is a powerful design tool, but relying solely on color to convey information creates accessibility barriers for users with color blindness or low vision. In this post, we'll explore how to make your SwiftUI apps more accessible by ensuring that information is communicated through multiple visual channels, not just color alone.&lt;/p&gt;
&lt;h2&gt;What is Differentiate Without Color?&lt;/h2&gt;
&lt;p&gt;Differentiate Without Color is an accessibility feature that helps users who have difficulty distinguishing colors. Approximately 8% of men and 0.5% of women have some form of color vision deficiency, making it essential to provide alternative ways to convey information.&lt;/p&gt;
&lt;p&gt;When users enable "Differentiate Without Color" in their accessibility settings, your app should supplement color-based information with additional visual indicators such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Icons and symbols&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Text labels&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Patterns and textures&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Shapes and borders&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Position and size differences&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example, instead of using only red and green to show error and success states, you should also include an X icon for errors and a checkmark for success.&lt;/p&gt;
&lt;h2&gt;How to Implement in SwiftUI&lt;/h2&gt;
&lt;p&gt;SwiftUI provides the &lt;code&gt;accessibilityDifferentiateWithoutColor&lt;/code&gt; environment variable to detect when users have enabled this setting. You can use this to adapt your UI accordingly.&lt;/p&gt;
&lt;h3&gt;Checking the Environment Variable&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ContentView: View {
    @Environment(\.accessibilityDifferentiateWithoutColor) 
    var differentiateWithoutColor

    var body: some View {
        VStack {
            if differentiateWithoutColor {
                Text("Differentiate Without Color is enabled")
            } else {
                Text("Using standard color scheme")
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Bad Practice: Color Only&lt;/h3&gt;
&lt;p&gt;Here's an example of what NOT to do - using color alone to convey status:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct BadStatusView: View {
    let isSuccess: Bool

    var body: some View {
        Text("Operation Complete")
            .padding()
            .background(isSuccess ? Color.green : Color.red)
            .cornerRadius(8)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This design fails because color-blind users may not be able to distinguish between the red and green states.&lt;/p&gt;
&lt;h3&gt;Good Practice: Color + Icons&lt;/h3&gt;
&lt;p&gt;Here's a better approach that uses both color and icons:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct GoodStatusView: View {
    @Environment(\.accessibilityDifferentiateWithoutColor) 
    var differentiateWithoutColor
    let isSuccess: Bool

    var statusIcon: String {
        isSuccess ? "checkmark.circle.fill" : "xmark.circle.fill"
    }

    var statusColor: Color {
        isSuccess ? .green : .red
    }

    var body: some View {
        HStack {
            Image(systemName: statusIcon)
                .foregroundColor(differentiateWithoutColor ? .primary : statusColor)

            Text(isSuccess ? "Success" : "Error")
                .foregroundColor(differentiateWithoutColor ? .primary : statusColor)
        }
        .padding()
        .background(
            RoundedRectangle(cornerRadius: 8)
                .stroke(differentiateWithoutColor ? Color.primary : statusColor, lineWidth: 2)
        )
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Chart Example with Patterns&lt;/h3&gt;
&lt;p&gt;When creating charts or graphs, use patterns in addition to colors:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct AccessibleChartView: View {
    @Environment(\.accessibilityDifferentiateWithoutColor) 
    var differentiateWithoutColor

    let data: [(String, Double, String)] = [
        ("Category A", 0.3, "circle.fill"),
        ("Category B", 0.5, "square.fill"),
        ("Category C", 0.2, "triangle.fill")
    ]

    var body: some View {
        VStack(spacing: 20) {
            ForEach(data, id: \.0) { item in
                HStack {
                    // Icon helps differentiate
                    if differentiateWithoutColor {
                        Image(systemName: item.2)
                            .frame(width: 30)
                    }

                    Text(item.0)
                        .frame(width: 100, alignment: .leading)

                    // Visual bar
                    Rectangle()
                        .fill(colorForCategory(item.0))
                        .frame(width: CGFloat(item.1) * 200, height: 30)
                        .overlay(
                            // Add pattern when needed
                            differentiateWithoutColor ? 
                                Image(systemName: item.2)
                                    .resizable()
                                    .scaledToFit()
                                    .padding(4) : nil
                        )
                }
            }
        }
        .padding()
    }

    func colorForCategory(_ category: String) -&amp;gt; Color {
        switch category {
        case "Category A": return .blue
        case "Category B": return .orange
        case "Category C": return .purple
        default: return .gray
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Button States Example&lt;/h3&gt;
&lt;p&gt;For interactive elements like buttons, combine color with shape and text:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct AccessibleButton: View {
    @Environment(\.accessibilityDifferentiateWithoutColor) 
    var differentiateWithoutColor

    let isActive: Bool
    let action: () -&amp;gt; Void

    var body: some View {
        Button(action: action) {
            HStack {
                // Always show text
                Text(isActive ? "Active" : "Inactive")

                // Add icon for clarity
                Image(systemName: isActive ? "checkmark.circle" : "circle")
            }
            .padding()
            .background(
                RoundedRectangle(cornerRadius: 8)
                    .fill(isActive &amp;amp;&amp;amp; !differentiateWithoutColor ? 
                          Color.blue : Color.gray.opacity(0.2))
                    .overlay(
                        RoundedRectangle(cornerRadius: 8)
                            .stroke(isActive ? Color.primary : Color.gray, 
                                   lineWidth: differentiateWithoutColor ? 3 : 1)
                    )
            )
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Best Practices&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Always provide multiple indicators&lt;/strong&gt;: Never rely on color alone. Use icons, labels, patterns, or shapes alongside color.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Test with color blindness simulators&lt;/strong&gt;: Use tools to see how your app appears to users with different types of color vision deficiency.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Use semantic colors&lt;/strong&gt;: SwiftUI's semantic colors (&lt;code&gt;.primary&lt;/code&gt;, &lt;code&gt;.secondary&lt;/code&gt;, &lt;code&gt;.accentColor&lt;/code&gt;) adapt to user preferences automatically.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Provide clear labels&lt;/strong&gt;: Text labels should always accompany color-coded information.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Consider contrast&lt;/strong&gt;: Even when adding icons, maintain sufficient color contrast for users with low vision.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Respect user preferences&lt;/strong&gt;: Check the &lt;code&gt;accessibilityDifferentiateWithoutColor&lt;/code&gt; environment variable and adapt your UI accordingly.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;Color alone is never enough to convey meaning — pair it with icons, labels, or patterns. The &lt;code&gt;accessibilityDifferentiateWithoutColor&lt;/code&gt; environment variable makes it straightforward to adapt your UI when users have this preference enabled.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/tag/Accessibility"&gt;More posts about Accessibility&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/accessibility-fundamentals"&gt;Accessibility Fundamentals&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/design/human-interface-guidelines/accessibility#Color-and-effects"&gt;Color and Contrast Guidelines&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/environmentvalues/accessibilitydifferentiatewithoutcolor"&gt;SwiftUI Environment Values&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/help/app-store-connect/manage-app-accessibility/differentiate-without-color-alone-evaluation-criteria"&gt;https://developer.apple.com/help/app-store-connect/manage-app-accessibility/differentiate-without-color-alone-evaluation-criteria&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>VoiceOver</title>    <link>https://wesleydegroot.nl/blog/voiceover</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/voiceover</guid>
    <pubDate>Mon, 30 Mar 2026 16:57:34 +0200</pubDate>
    <category>Accessibility</category>
    <category>SwiftUI</category>
    <description>&lt;p&gt;VoiceOver is Apple's screen reader that enables blind and low vision users to navigate and interact with iOS, iPadOS, and macOS devices. In this post, we'll explore how to make your SwiftUI apps fully accessible to VoiceOver users by providing clear, descriptive labels and implementing proper accessibility support.&lt;/p&gt;
&lt;h2&gt;What is VoiceOver?&lt;/h2&gt;
&lt;p&gt;VoiceOver is a gesture-based screen reader that speaks aloud the elements on the screen, allowing users who are blind or have low vision to use their devices without seeing the display. Users navigate through interface elements sequentially, and VoiceOver announces what each element is and what it does.&lt;/p&gt;
&lt;p&gt;VoiceOver is used by millions of people worldwide and is considered one of the most powerful assistive technologies built into modern operating systems. When a user touches an element on screen (or focuses it with keyboard navigation), VoiceOver announces:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;What the element is (button, text field, image, etc.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The element's label or content&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The element's state (selected, disabled, etc.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A hint about how to interact with it (if provided)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;How to Implement VoiceOver Support in SwiftUI&lt;/h2&gt;
&lt;p&gt;SwiftUI provides excellent default VoiceOver support, but you can enhance it with accessibility modifiers to create an even better experience.&lt;/p&gt;
&lt;h3&gt;Basic Accessibility Labels&lt;/h3&gt;
&lt;p&gt;Use &lt;code&gt;.accessibilityLabel(_:)&lt;/code&gt; to provide descriptive text for elements that don't have clear text content:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct BasicAccessibilityView: View {
    var body: some View {
        VStack(spacing: 20) {
            // Bad: No context for icon-only button
            Button(action: shareContent) {
                Image(systemName: "square.and.arrow.up")
            }

            // Good: Clear label for VoiceOver users
            Button(action: shareContent) {
                Image(systemName: "square.and.arrow.up")
            }
            .accessibilityLabel("Share")
        }
    }

    func shareContent() {
        print("Sharing content")
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Accessibility Hints&lt;/h3&gt;
&lt;p&gt;Use &lt;code&gt;.accessibilityHint(_:)&lt;/code&gt; to provide additional context about what will happen when the user interacts with an element:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct HintExample: View {
    @State private var itemCount = 0

    var body: some View {
        VStack(spacing: 20) {
            Text("Items: \(itemCount)")

            Button("Add Item") {
                itemCount += 1
            }
            .accessibilityLabel("Add Item")
            .accessibilityHint("Increases the item count by one")

            Button(action: clearItems) {
                Image(systemName: "trash")
            }
            .accessibilityLabel("Clear all items")
            .accessibilityHint("Removes all items from your collection")
        }
    }

    func clearItems() {
        itemCount = 0
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Grouping Accessibility Elements&lt;/h3&gt;
&lt;p&gt;Use &lt;code&gt;.accessibilityElement(children:)&lt;/code&gt; to group related elements so VoiceOver reads them as a single unit:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct CardView: View {
    let title: String
    let subtitle: String
    let description: String

    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
            Text(title)
                .font(.headline)

            Text(subtitle)
                .font(.subheadline)
                .foregroundColor(.secondary)

            Text(description)
                .font(.body)
        }
        .padding()
        .background(Color.gray.opacity(0.1))
        .cornerRadius(12)
        // Combine all text into one announcement
        .accessibilityElement(children: .combine)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For more complex grouping:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct ProfileCard: View {
    let name: String
    let role: String
    let isOnline: Bool

    var body: some View {
        HStack(spacing: 16) {
            Image(systemName: "person.circle.fill")
                .resizable()
                .frame(width: 50, height: 50)
                .accessibilityHidden(true) // Decorative, hide from VoiceOver

            VStack(alignment: .leading) {
                Text(name)
                    .font(.headline)

                Text(role)
                    .font(.subheadline)
                    .foregroundColor(.secondary)

                HStack {
                    Circle()
                        .fill(isOnline ? Color.green : Color.gray)
                        .frame(width: 8, height: 8)

                    Text(isOnline ? "Online" : "Offline")
                        .font(.caption)
                }
            }
        }
        .padding()
        .accessibilityElement(children: .combine)
        .accessibilityLabel("\(name), \(role), \(isOnline ? "Online" : "Offline")")
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Custom Controls and Views&lt;/h3&gt;
&lt;p&gt;For custom interactive controls, make sure to provide all necessary accessibility information:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct CustomSlider: View {
    @State private var value: Double = 50

    var body: some View {
        VStack {
            Text("Volume: \(Int(value))%")

            Slider(value: $value, in: 0...100, step: 1)
                .accessibilityLabel("Volume")
                .accessibilityValue("\(Int(value)) percent")
                .accessibilityHint("Adjusts the volume level")
        }
        .padding()
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For custom views with complex interactions:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct CustomRatingControl: View {
    @Binding var rating: Int
    let maxRating: Int = 5

    var body: some View {
        HStack {
            ForEach(1...maxRating, id: \.self) { star in
                Image(systemName: star &amp;lt;= rating ? "star.fill" : "star")
                    .foregroundColor(star &amp;lt;= rating ? .yellow : .gray)
                    .onTapGesture {
                        rating = star
                    }
            }
        }
        // Combine all stars into one accessibility element
        .accessibilityElement(children: .ignore)
        .accessibilityLabel("Rating")
        .accessibilityValue("\(rating) out of \(maxRating) stars")
        .accessibilityAdjustableAction { direction in
            switch direction {
            case .increment:
                if rating &amp;lt; maxRating { rating += 1 }
            case .decrement:
                if rating &amp;gt; 0 { rating -= 1 }
            @unknown default:
                break
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Form Fields&lt;/h3&gt;
&lt;p&gt;Properly label form fields and provide clear error messages:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct AccessibleForm: View {
    @State private var email = ""
    @State private var password = ""
    @State private var emailError: String?

    var body: some View {
        Form {
            Section {
                TextField("Email", text: $email)
                    .textContentType(.emailAddress)
                    .keyboardType(.emailAddress)
                    .accessibilityLabel("Email address")
                    .accessibilityHint("Enter your email address")

                if let error = emailError {
                    Text(error)
                        .foregroundColor(.red)
                        .font(.caption)
                        .accessibilityLabel("Email error: \(error)")
                }

                SecureField("Password", text: $password)
                    .textContentType(.password)
                    .accessibilityLabel("Password")
                    .accessibilityHint("Enter your password")
            }

            Section {
                Button("Sign In") {
                    validateAndSignIn()
                }
                .accessibilityLabel("Sign In")
                .accessibilityHint("Submits the form to sign in")
            }
        }
    }

    func validateAndSignIn() {
        if !email.contains("@") {
            emailError = "Please enter a valid email address"
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Lists and Dynamic Content&lt;/h3&gt;
&lt;p&gt;For lists with many items, provide meaningful labels:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct MessageList: View {
    let messages: [Message]

    var body: some View {
        List(messages) { message in
            MessageRow(message: message)
                .accessibilityElement(children: .combine)
                .accessibilityLabel("""
                    Message from \(message.sender), \
                    received \(message.time), \
                    \(message.isRead ? "read" : "unread")
                    """)
                .accessibilityHint("Double tap to open message")
        }
    }
}

struct Message: Identifiable {
    let id = UUID()
    let sender: String
    let preview: String
    let time: String
    let isRead: Bool
}

struct MessageRow: View {
    let message: Message

    var body: some View {
        HStack {
            VStack(alignment: .leading) {
                Text(message.sender)
                    .font(.headline)
                Text(message.preview)
                    .font(.subheadline)
                    .foregroundColor(.secondary)
            }

            Spacer()

            VStack(alignment: .trailing) {
                Text(message.time)
                    .font(.caption)

                if !message.isRead {
                    Circle()
                        .fill(Color.blue)
                        .frame(width: 8, height: 8)
                }
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Best Practices for VoiceOver Support&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Be Descriptive but Concise&lt;/strong&gt;: Labels should be clear and to the point. Avoid redundancy (don't say "button" - VoiceOver announces the element type).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Provide Context&lt;/strong&gt;: Use hints to explain what will happen when the user interacts with an element, especially for non-obvious actions.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Hide Decorative Elements&lt;/strong&gt;: Use &lt;code&gt;.accessibilityHidden(true)&lt;/code&gt; for purely decorative images and icons.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Group Related Content&lt;/strong&gt;: Use &lt;code&gt;.accessibilityElement(children: .combine)&lt;/code&gt; to group related elements that should be read as one unit.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Update Accessibility Info Dynamically&lt;/strong&gt;: When content changes, make sure accessibility labels and values update too.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Test with VoiceOver&lt;/strong&gt;: Always test your app with VoiceOver enabled. Enable it in Settings &amp;gt; Accessibility &amp;gt; VoiceOver, or use the triple-click home/side button shortcut.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Announce State Changes&lt;/strong&gt;: For dynamic content that changes without user interaction, consider using accessibility notifications to alert VoiceOver users.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Avoid Long Labels&lt;/strong&gt;: Keep accessibility labels under 2-3 sentences. Very long labels can be frustrating for VoiceOver users.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;VoiceOver support starts with good labels and traits. Test with VoiceOver turned on — navigating your own app with it running is the fastest way to find gaps you wouldn't spot otherwise.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/tag/Accessibility"&gt;More posts about Accessibility&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/accessibility-fundamentals"&gt;Accessibility Fundamentals&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/accessibility/voiceover"&gt;VoiceOver Programming Guide&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view-accessibility"&gt;SwiftUI Accessibility Modifiers&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/help/app-store-connect/manage-app-accessibility/voiceover-evaluation-criteria"&gt;https://developer.apple.com/help/app-store-connect/manage-app-accessibility/voiceover-evaluation-criteria&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Voice Control</title>    <link>https://wesleydegroot.nl/blog/voice-control</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/voice-control</guid>
    <pubDate>Mon, 30 Mar 2026 16:57:35 +0200</pubDate>
    <category>Accessibility</category>
    <category>SwiftUI</category>
    <description>&lt;p&gt;Voice Control enables users to navigate and interact with their devices entirely through voice commands, without touching the screen or using a keyboard. In this post, we'll explore how to ensure your SwiftUI apps work seamlessly with Voice Control, making them accessible to users with motor impairments and those who prefer hands-free interaction.&lt;/p&gt;
&lt;h2&gt;What is Voice Control?&lt;/h2&gt;
&lt;p&gt;Voice Control is an accessibility feature that allows users to control their devices using only their voice. Unlike VoiceOver (which is for blind users), Voice Control is designed for users who have difficulty with physical interaction—such as those with motor impairments, repetitive strain injuries, or temporary injuries.&lt;/p&gt;
&lt;p&gt;With Voice Control enabled, users can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Navigate through apps by saying "Tap [label]"&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Speak numbers that appear on screen elements&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use voice commands for gestures like scrolling and swiping&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Dictate text into fields&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Control the entire device without physical touch&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Voice Control displays numbers or names over interactive elements, allowing users to precisely target what they want to interact with by voice.&lt;/p&gt;
&lt;h2&gt;How Voice Control Differs from VoiceOver&lt;/h2&gt;
&lt;p&gt;While both are voice-based accessibility features, they serve different purposes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;VoiceOver&lt;/strong&gt;: Screen reader for blind users. Reads content aloud and uses gestures for navigation.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Voice Control&lt;/strong&gt;: Hands-free navigation for users with motor impairments. Users speak commands to interact with visible interface elements.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Understanding this distinction is crucial for proper implementation. Voice Control users can see the screen but may not be able to touch it effectively.&lt;/p&gt;
&lt;h2&gt;How to Implement Voice Control Support in SwiftUI&lt;/h2&gt;
&lt;p&gt;The good news is that most Voice Control support comes from proper VoiceOver support. However, there are specific considerations for Voice Control users.&lt;/p&gt;
&lt;h3&gt;Basic Button Accessibility&lt;/h3&gt;
&lt;p&gt;Voice Control relies on accessibility labels to identify elements. Users speak the label to interact with an element:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct VoiceControlExample: View {
    var body: some View {
        VStack(spacing: 20) {
            // User can say "Tap Submit"
            Button("Submit") {
                submitForm()
            }

            // For icon-only buttons, provide a clear label
            Button(action: refreshData) {
                Image(systemName: "arrow.clockwise")
            }
            .accessibilityLabel("Refresh")
            // User can say "Tap Refresh"

            // Complex button with multiple elements
            Button(action: shareContent) {
                HStack {
                    Image(systemName: "square.and.arrow.up")
                    Text("Share")
                }
            }
            .accessibilityLabel("Share")
            .accessibilityHint("Opens the share sheet")
        }
    }

    func submitForm() {
        print("Form submitted")
    }

    func refreshData() {
        print("Refreshing data...")
    }

    func shareContent() {
        print("Sharing content")
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Using &lt;code&gt;.accessibilityInputLabels(_:)&lt;/code&gt; for Alternative Commands&lt;/h3&gt;
&lt;p&gt;Voice Control users might try different phrases to interact with your buttons. Use &lt;code&gt;.accessibilityInputLabels(_:)&lt;/code&gt; to provide alternative voice commands:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct AlternativeLabelsExample: View {
    var body: some View {
        VStack(spacing: 20) {
            // User can say "Tap Save", "Tap Submit", or "Tap Send"
            Button("Save") {
                saveDocument()
            }
            .accessibilityLabel("Save")
            .accessibilityInputLabels(["Save", "Submit", "Send"])

            // User can say "Tap Delete", "Tap Remove", or "Tap Trash"
            Button(action: deleteItem) {
                Image(systemName: "trash")
            }
            .accessibilityLabel("Delete")
            .accessibilityInputLabels(["Delete", "Remove", "Trash"])

            // Shopping cart with multiple commands
            Button(action: checkout) {
                HStack {
                    Image(systemName: "cart")
                    Text("Cart")
                }
            }
            .accessibilityLabel("Shopping Cart")
            .accessibilityInputLabels([
                "Shopping Cart",
                "Cart",
                "Basket",
                "Checkout"
            ])
        }
    }

    func saveDocument() {
        print("Document saved")
    }

    func deleteItem() {
        print("Item deleted")
    }

    func checkout() {
        print("Checking out...")
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Interactive Form Elements&lt;/h3&gt;
&lt;p&gt;For forms, ensure all interactive elements have clear, speakable labels:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct VoiceControlForm: View {
    @State private var username = ""
    @State private var email = ""
    @State private var agreeToTerms = false

    var body: some View {
        Form {
            Section(header: Text("Account Information")) {
                TextField("Username", text: $username)
                    .accessibilityLabel("Username")
                    .accessibilityInputLabels(["Username", "User name", "Login name"])

                TextField("Email", text: $email)
                    .accessibilityLabel("Email address")
                    .accessibilityInputLabels(["Email", "Email address", "E-mail"])
            }

            Section {
                Toggle("I agree to the terms and conditions", isOn: $agreeToTerms)
                    .accessibilityLabel("Agree to terms")
                    .accessibilityInputLabels([
                        "Agree to terms",
                        "Terms and conditions",
                        "Accept terms"
                    ])
            }

            Section {
                Button("Create Account") {
                    createAccount()
                }
                .accessibilityLabel("Create Account")
                .accessibilityInputLabels([
                    "Create Account",
                    "Sign up",
                    "Register"
                ])
            }
        }
    }

    func createAccount() {
        guard !username.isEmpty, !email.isEmpty, agreeToTerms else {
            print("Please fill in all fields and agree to the terms")
            return
        }
        print("Account created for \(username)")
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Custom Controls&lt;/h3&gt;
&lt;p&gt;For custom controls, ensure they're focusable and have appropriate labels:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct CustomStarRating: View {
    @Binding var rating: Int
    let maxRating: Int = 5

    var body: some View {
        HStack(spacing: 12) {
            ForEach(1...maxRating, id: \.self) { index in
                Button(action: {
                    rating = index
                }) {
                    Image(systemName: index &amp;lt;= rating ? "star.fill" : "star")
                        .foregroundColor(index &amp;lt;= rating ? .yellow : .gray)
                        .font(.title)
                }
                .accessibilityLabel("\(index) star\(index == 1 ? "" : "s")")
                .accessibilityInputLabels([
                    "\(index) star\(index == 1 ? "" : "s")",
                    "Star \(index)",
                    "\(index)"
                ])
                .accessibilityHint("Sets rating to \(index) out of \(maxRating)")
            }
        }
        .accessibilityElement(children: .contain)
    }
}

struct RatingView: View {
    @State private var rating = 0

    var body: some View {
        VStack {
            Text("Rate this item")
                .font(.headline)

            CustomStarRating(rating: $rating)

            Text("Current rating: \(rating) stars")
                .font(.subheadline)
        }
        .padding()
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Navigation and Lists&lt;/h3&gt;
&lt;p&gt;For lists and navigation, ensure each item is clearly labeled:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct VoiceControlList: View {
    let items = ["Home", "Settings", "Profile", "Help", "About"]

    var body: some View {
        NavigationView {
            List {
                ForEach(items, id: \.self) { item in
                    NavigationLink(destination: DetailView(title: item)) {
                        HStack {
                            Image(systemName: iconForItem(item))
                            Text(item)
                        }
                    }
                    .accessibilityLabel(item)
                    .accessibilityInputLabels([item, "Go to \(item)"])
                }
            }
            .navigationTitle("Menu")
        }
    }

    func iconForItem(_ item: String) -&amp;gt; String {
        switch item {
        case "Home": return "house"
        case "Settings": return "gear"
        case "Profile": return "person"
        case "Help": return "questionmark.circle"
        case "About": return "info.circle"
        default: return "doc"
        }
    }
}

struct DetailView: View {
    let title: String

    var body: some View {
        Text("\(title) Details")
            .navigationTitle(title)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Context Menus and Actions&lt;/h3&gt;
&lt;p&gt;Ensure context menu items are accessible with Voice Control:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct ContextMenuExample: View {
    @State private var selectedItem: String?

    var body: some View {
        VStack {
            Text("Long press or say 'Show Actions' for the box")
                .padding()

            RoundedRectangle(cornerRadius: 12)
                .fill(Color.blue)
                .frame(width: 200, height: 200)
                .contextMenu {
                    Button(action: copyAction) {
                        Label("Copy", systemImage: "doc.on.doc")
                    }
                    .accessibilityLabel("Copy")

                    Button(action: shareAction) {
                        Label("Share", systemImage: "square.and.arrow.up")
                    }
                    .accessibilityLabel("Share")

                    Button(role: .destructive, action: deleteAction) {
                        Label("Delete", systemImage: "trash")
                    }
                    .accessibilityLabel("Delete")
                }
                .accessibilityLabel("Content box")
                .accessibilityHint("Long press for actions menu")
        }
    }

    func copyAction() {
        print("Item copied to clipboard")
    }

    func shareAction() {
        print("Sharing item...")
    }

    func deleteAction() {
        print("Item deleted")
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Best Practices for Voice Control&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Use Clear, Speakable Labels&lt;/strong&gt;: Choose labels that are easy to pronounce and remember. Avoid technical jargon when possible.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Provide Alternative Input Labels&lt;/strong&gt;: Use &lt;code&gt;.accessibilityInputLabels(_:)&lt;/code&gt; to give users multiple ways to refer to the same element.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Keep Labels Short&lt;/strong&gt;: Shorter labels are easier to speak. "Save" is better than "Save Document to Cloud Storage".&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Avoid Ambiguous Labels&lt;/strong&gt;: Don't have multiple buttons with the same label. Voice Control users need unique identifiers.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Test Common Phrases&lt;/strong&gt;: Think about what users might naturally say to interact with your elements and include those as input labels.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Make All Interactive Elements Accessible&lt;/strong&gt;: Every button, link, and control should have a proper accessibility label.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Use Descriptive Text&lt;/strong&gt;: When possible, use actual text labels instead of just icons. This makes Voice Control more intuitive.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Group Related Actions&lt;/strong&gt;: For complex views, consider grouping related actions under a single accessible element.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Test with Voice Control&lt;/strong&gt;: Enable Voice Control (Settings &amp;gt; Accessibility &amp;gt; Voice Control) and try navigating your app using only your voice.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Testing Voice Control&lt;/h2&gt;
&lt;p&gt;To test your app with Voice Control:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Go to Settings &amp;gt; Accessibility &amp;gt; Voice Control&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Turn on Voice Control&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A microphone icon appears when Voice Control is listening&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Say "Show numbers" to see numbered overlays on interactive elements&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Say "Show names" to see text labels on interactive elements&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Try saying "Tap [label]" for your buttons&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Common voice commands:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;"Tap [name]" - Activates a button or control&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;"Show numbers" / "Show names" - Displays overlays&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;"Scroll down" / "Scroll up" - Scrolls content&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;"Go home" - Returns to home screen&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;"Open [app name]" - Opens an app&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;Voice Control mostly works automatically if you've already handled VoiceOver. The main thing to add is &lt;code&gt;.accessibilityInputLabels(_:)&lt;/code&gt; on elements where the visible label doesn't match what a user would naturally say.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/tag/Accessibility"&gt;More posts about Accessibility&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/accessibility-fundamentals"&gt;Accessibility Fundamentals&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/accessibility/voice_control"&gt;Voice Control Programming Guide&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/accessibilityinputlabels(_:)-4mhp0"&gt;SwiftUI Accessibility Input Labels&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/help/app-store-connect/manage-app-accessibility/voice-control-evaluation-criteria"&gt;https://developer.apple.com/help/app-store-connect/manage-app-accessibility/voice-control-evaluation-criteria&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Captions</title>    <link>https://wesleydegroot.nl/blog/captions</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/captions</guid>
    <pubDate>Mon, 30 Mar 2026 16:57:35 +0200</pubDate>
    <category>Accessibility</category>
    <category>SwiftUI</category>
    <description>&lt;p&gt;Captions and subtitles make video and audio content accessible to deaf and hard of hearing users, as well as non-native speakers and users in sound-sensitive environments. In this post, we'll explore how to implement caption support in your SwiftUI apps and respect user preferences for displaying captions.&lt;/p&gt;
&lt;h2&gt;What are Captions?&lt;/h2&gt;
&lt;p&gt;Captions (also called subtitles or closed captions) are text overlays that display spoken dialogue, sound effects, and other audio information in video content. They enable:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Deaf and hard of hearing users&lt;/strong&gt; to access audio content&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Non-native speakers&lt;/strong&gt; to better understand dialogue&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Users in quiet environments&lt;/strong&gt; where audio can't be played&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Users in noisy environments&lt;/strong&gt; where audio is hard to hear&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Language learners&lt;/strong&gt; to improve comprehension&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Users who prefer reading&lt;/strong&gt; to listening&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are two types of captions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Closed Captions (CC)&lt;/strong&gt;: Can be turned on/off by the user&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Open Captions&lt;/strong&gt;: Always visible, burned into the video&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In accessible apps, you should always provide closed captions so users can choose whether to display them.&lt;/p&gt;
&lt;h2&gt;Detecting User Caption Preferences&lt;/h2&gt;
&lt;p&gt;iOS provides an accessibility setting that allows users to indicate they prefer captions. You can detect this preference using the &lt;code&gt;accessibilityShowsClosedCaptions&lt;/code&gt; environment variable:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct CaptionPreferenceView: View {
    @Environment(\.accessibilityShowsClosedCaptions) 
    var showCaptions

    var body: some View {
        VStack(spacing: 20) {
            Text("Caption Preference")
                .font(.headline)

            if showCaptions {
                Text("✓ User prefers captions enabled")
                    .foregroundColor(.green)
            } else {
                Text("User has not enabled caption preference")
                    .foregroundColor(.secondary)
            }
        }
        .padding()
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Adding Captions to Video in SwiftUI&lt;/h2&gt;
&lt;p&gt;SwiftUI uses AVFoundation for video playback, which has built-in support for captions through subtitle tracks. Here's how to implement it:&lt;/p&gt;
&lt;h3&gt;Basic Video Player with Caption Support&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI
import AVKit

struct VideoPlayerWithCaptions: View {
    @Environment(\.accessibilityShowsClosedCaptions) 
    var showCaptions

    @State private var player: AVPlayer?

    var body: some View {
        VStack {
            if let player = player {
                VideoPlayer(player: player)
                    .frame(height: 300)
                    .onAppear {
                        configureCaptions(for: player)
                    }
            } else {
                ProgressView("Loading video...")
                    .frame(height: 300)
            }

            Text("Video with captions")
                .font(.headline)
                .padding()
        }
        .onAppear {
            setupPlayer()
        }
    }

    func setupPlayer() {
        // Replace with your video URL
        guard let videoURL = Bundle.main.url(
            forResource: "sample",
            withExtension: "mp4"
        ) else { return }

        player = AVPlayer(url: videoURL)
    }

    func configureCaptions(for player: AVPlayer) {
        // Enable captions based on user preference
        guard let currentItem = player.currentItem else { return }

        // Get available media selection options
        let legibleGroup = currentItem.asset.mediaSelectionGroup(
            forMediaCharacteristic: .legible
        )

        if let legibleGroup = legibleGroup {
            // Find the appropriate caption track
            let selectedOption: AVMediaSelectionOption?

            if showCaptions {
                // Select caption track if available
                selectedOption = legibleGroup.options.first
            } else {
                // No captions
                selectedOption = nil
            }

            currentItem.select(selectedOption, in: legibleGroup)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Advanced Video Player with Caption Controls&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct AdvancedVideoPlayer: View {
    @Environment(\.accessibilityShowsClosedCaptions) 
    var userPrefersClosedCaptions

    @State private var player: AVPlayer?
    @State private var showCaptions: Bool = false
    @State private var availableCaptionLanguages: [String] = []
    @State private var selectedCaptionLanguage: String?

    var body: some View {
        VStack(spacing: 0) {
            if let player = player {
                VideoPlayer(player: player)
                    .frame(height: 300)
            }

            // Caption controls
            VStack(alignment: .leading, spacing: 16) {
                Toggle("Show Captions", isOn: $showCaptions)
                    .onChange(of: showCaptions) { newValue in
                        updateCaptions()
                    }

                if showCaptions &amp;amp;&amp;amp; !availableCaptionLanguages.isEmpty {
                    VStack(alignment: .leading, spacing: 8) {
                        Text("Caption Language")
                            .font(.subheadline)
                            .foregroundColor(.secondary)

                        Picker("Language", selection: $selectedCaptionLanguage) {
                            ForEach(availableCaptionLanguages, id: \.self) { lang in
                                Text(lang).tag(lang as String?)
                            }
                        }
                        .pickerStyle(MenuPickerStyle())
                        .onChange(of: selectedCaptionLanguage) { _ in
                            updateCaptions()
                        }
                    }
                }
            }
            .padding()
        }
        .onAppear {
            setupPlayer()
            // Respect user's system preference
            showCaptions = userPrefersClosedCaptions
        }
    }

    func setupPlayer() {
        guard let videoURL = Bundle.main.url(
            forResource: "sample",
            withExtension: "mp4"
        ) else { return }

        player = AVPlayer(url: videoURL)

        // Load available caption languages
        loadAvailableCaptions()
    }

    func loadAvailableCaptions() {
        guard let currentItem = player?.currentItem else { return }

        let legibleGroup = currentItem.asset.mediaSelectionGroup(
            forMediaCharacteristic: .legible
        )

        if let options = legibleGroup?.options {
            availableCaptionLanguages = options.compactMap { option in
                option.displayName
            }

            if !availableCaptionLanguages.isEmpty {
                selectedCaptionLanguage = availableCaptionLanguages.first
            }
        }
    }

    func updateCaptions() {
        guard let currentItem = player?.currentItem,
              let legibleGroup = currentItem.asset.mediaSelectionGroup(
                forMediaCharacteristic: .legible
              ) else { return }

        if showCaptions, let selectedLang = selectedCaptionLanguage {
            // Find and select the caption track matching the language
            let option = legibleGroup.options.first { option in
                option.displayName == selectedLang
            }
            currentItem.select(option, in: legibleGroup)
        } else {
            // Turn off captions
            currentItem.select(nil, in: legibleGroup)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Creating Caption Files&lt;/h2&gt;
&lt;p&gt;Captions are typically provided in WebVTT (.vtt) or SRT (.srt) format. Here's an example WebVTT file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;WEBVTT

00:00:00.000 --&amp;gt; 00:00:02.000
Welcome to our app tutorial.

00:00:02.500 --&amp;gt; 00:00:05.000
Today we'll show you how to get started.

00:00:05.500 --&amp;gt; 00:00:08.000
[upbeat music playing]

00:00:08.500 --&amp;gt; 00:00:12.000
First, tap the plus button to create a new project.&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Adding Caption Files to Your Video&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import AVFoundation

func createPlayerItemWithCaptions(
    videoURL: URL,
    captionURL: URL
) -&amp;gt; AVPlayerItem {
    let asset = AVURLAsset(url: videoURL)
    let playerItem = AVPlayerItem(asset: asset)

    // Create a mutable composition
    let composition = AVMutableComposition()

    // Add video track
    guard let videoTrack = asset.tracks(
        withMediaType: .video
    ).first else {
        return playerItem
    }

    let compositionVideoTrack = composition.addMutableTrack(
        withMediaType: .video,
        preferredTrackID: kCMPersistentTrackID_Invalid
    )

    try? compositionVideoTrack?.insertTimeRange(
        CMTimeRangeMake(start: .zero, duration: asset.duration),
        of: videoTrack,
        at: .zero
    )

    // Add caption track
    let subtitleAsset = AVURLAsset(url: captionURL)

    if let subtitleTrack = subtitleAsset.tracks(
        withMediaType: .text
    ).first {
        let compositionSubtitleTrack = composition.addMutableTrack(
            withMediaType: .text,
            preferredTrackID: kCMPersistentTrackID_Invalid
        )

        try? compositionSubtitleTrack?.insertTimeRange(
            CMTimeRangeMake(start: .zero, duration: asset.duration),
            of: subtitleTrack,
            at: .zero
        )
    }

    return AVPlayerItem(asset: composition)
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Best Practices for Captions&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Respect User Preferences&lt;/strong&gt;: Always check &lt;code&gt;accessibilityShowsClosedCaptions&lt;/code&gt; and enable captions by default if the user has expressed this preference.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Provide Accurate Timing&lt;/strong&gt;: Captions should appear in sync with the audio, ideally staying on screen long enough to be read comfortably.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Include Sound Effects&lt;/strong&gt;: Caption important non-speech sounds like [door closes], [applause], or [suspenseful music].&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Identify Speakers&lt;/strong&gt;: When multiple people speak, identify who is talking, especially if they're off-screen.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Use Standard Formatting&lt;/strong&gt;: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Keep lines to 32-42 characters&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Maximum 2 lines per caption&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Display for 1-7 seconds based on reading speed&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Provide Caption Controls&lt;/strong&gt;: Give users the ability to toggle captions on/off and select language if multiple tracks are available.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Test Readability&lt;/strong&gt;: Ensure captions are readable against your video content. Good players provide a semi-transparent background.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Support Multiple Languages&lt;/strong&gt;: Provide captions in multiple languages when possible for international users.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Quality Over Auto-Generation&lt;/strong&gt;: While auto-generated captions are better than nothing, human-reviewed captions are significantly more accurate and accessible.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Caption Styling&lt;/h2&gt;
&lt;p&gt;While native video players handle caption styling, if you're creating a custom player, ensure your captions have:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct CustomCaptionStyle {
    static let textColor = Color.white
    static let backgroundColor = Color.black.opacity(0.75)
    static let font = Font.system(size: 18, weight: .semibold)
    static let padding: CGFloat = 8
    static let cornerRadius: CGFloat = 4
}

struct CaptionOverlay: View {
    let text: String

    var body: some View {
        Text(text)
            .font(CustomCaptionStyle.font)
            .foregroundColor(CustomCaptionStyle.textColor)
            .padding(CustomCaptionStyle.padding)
            .background(
                RoundedRectangle(cornerRadius: CustomCaptionStyle.cornerRadius)
                    .fill(CustomCaptionStyle.backgroundColor)
            )
            .multilineTextAlignment(.center)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;Captions help deaf and hard of hearing users, but they also benefit people watching in noisy environments or learning a language — they're a usability feature as much as an accessibility one. Use &lt;code&gt;accessibilityShowsClosedCaptions&lt;/code&gt; to respect the user's system preference and enable them by default when it's set.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/tag/Accessibility"&gt;More posts about Accessibility&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/accessibility-fundamentals"&gt;Accessibility Fundamentals&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/avfoundation/media_playback/adding_subtitles_and_alternative_audio_tracks"&gt;AVFoundation Captions Programming Guide&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://www.w3.org/TR/webvtt1/"&gt;WebVTT Format Specification&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/help/app-store-connect/manage-app-accessibility/captions-evaluation-criteria"&gt;https://developer.apple.com/help/app-store-connect/manage-app-accessibility/captions-evaluation-criteria&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Audio Descriptions</title>    <link>https://wesleydegroot.nl/blog/audio-descriptions</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/audio-descriptions</guid>
    <pubDate>Mon, 30 Mar 2026 16:57:35 +0200</pubDate>
    <category>Accessibility</category>
    <category>SwiftUI</category>
    <description>&lt;p&gt;Audio descriptions provide narration of visual elements in video content, making it accessible to blind and low vision users. In this post, we'll explore what audio descriptions are, how to detect user preferences for them, and how to implement audio description support in your SwiftUI apps.&lt;/p&gt;
&lt;h2&gt;What are Audio Descriptions?&lt;/h2&gt;
&lt;p&gt;Audio descriptions (also called video descriptions or descriptive narration) are audio tracks that describe important visual information in videos during natural pauses in dialogue. They narrate:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Actions and gestures&lt;/strong&gt;: What characters are doing&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Scene changes&lt;/strong&gt;: Where the action takes place&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;On-screen text&lt;/strong&gt;: Titles, captions, or important text&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Expressions and emotions&lt;/strong&gt;: Non-verbal communication&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Visual effects&lt;/strong&gt;: Important visual elements that convey meaning&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Audio descriptions enable blind and low vision users to understand visual content they cannot see. For example, in a cooking video, audio descriptions might say: "She pours flour into a large mixing bowl, then cracks two eggs into the center."&lt;/p&gt;
&lt;h2&gt;Who Benefits from Audio Descriptions?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Blind users&lt;/strong&gt; who cannot see the video at all&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Low vision users&lt;/strong&gt; who may miss visual details&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Users with cognitive disabilities&lt;/strong&gt; who benefit from additional context&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Users in situations&lt;/strong&gt; where they can't watch the screen (driving, exercising, etc.)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Detecting User Preferences in SwiftUI&lt;/h2&gt;
&lt;p&gt;iOS provides accessibility settings for audio descriptions. You can detect if a user prefers audio descriptions using the &lt;code&gt;accessibilityDifferentiateWithoutColor&lt;/code&gt; environment variable (for older iOS versions) or by checking AVPlayer's accessibility options:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI
import AVFoundation

struct AudioDescriptionPreferenceView: View {
    @State private var userPrefersAudioDescriptions = false

    var body: some View {
        VStack(spacing: 20) {
            Text("Audio Description Settings")
                .font(.headline)

            if userPrefersAudioDescriptions {
                Text("✓ User prefers audio descriptions")
                    .foregroundColor(.green)
            } else {
                Text("Standard audio track will be used")
                    .foregroundColor(.secondary)
            }
        }
        .padding()
        .onAppear {
            checkAudioDescriptionPreference()
        }
    }

    func checkAudioDescriptionPreference() {
        // Check if user has enabled audio descriptions
        // This is typically done through AVFoundation's media selection
        userPrefersAudioDescriptions = AVAudioSession.sharedInstance()
            .currentRoute.outputs.contains { output in
                // Check for accessibility features
                output.portType == .headphones
            }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Implementing Audio Descriptions with AVFoundation&lt;/h2&gt;
&lt;p&gt;AVFoundation supports multiple audio tracks, allowing you to provide both standard audio and audio with descriptions:&lt;/p&gt;
&lt;h3&gt;Basic Video Player with Audio Description Support&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI
import AVKit

struct VideoPlayerWithAudioDescriptions: View {
    @State private var player: AVPlayer?
    @State private var hasAudioDescriptions = false

    var body: some View {
        VStack {
            if let player = player {
                VideoPlayer(player: player)
                    .frame(height: 300)
            }

            VStack(alignment: .leading, spacing: 16) {
                Text("Audio Track Options")
                    .font(.headline)

                if hasAudioDescriptions {
                    HStack {
                        Image(systemName: "checkmark.circle.fill")
                            .foregroundColor(.green)
                        Text("Audio descriptions available")
                    }
                } else {
                    HStack {
                        Image(systemName: "info.circle")
                            .foregroundColor(.secondary)
                        Text("Standard audio only")
                    }
                }
            }
            .padding()
        }
        .onAppear {
            setupPlayer()
        }
    }

    func setupPlayer() {
        guard let videoURL = Bundle.main.url(
            forResource: "sample_with_descriptions",
            withExtension: "mp4"
        ) else { return }

        let asset = AVURLAsset(url: videoURL)
        let playerItem = AVPlayerItem(asset: asset)
        player = AVPlayer(playerItem: playerItem)

        // Check for audio description tracks
        checkForAudioDescriptions(in: asset)

        // Configure audio descriptions
        configureAudioDescriptions(for: playerItem)
    }

    func checkForAudioDescriptions(in asset: AVAsset) {
        let audibleGroup = asset.mediaSelectionGroup(
            forMediaCharacteristic: .audible
        )

        if let audibleGroup = audibleGroup {
            // Check if any audio track has descriptions
            hasAudioDescriptions = audibleGroup.options.contains { option in
                option.hasMediaCharacteristic(.describesVideoForAccessibility)
            }
        }
    }

    func configureAudioDescriptions(for playerItem: AVPlayerItem) {
        guard let audibleGroup = playerItem.asset.mediaSelectionGroup(
            forMediaCharacteristic: .audible
        ) else { return }

        // Find audio track with descriptions
        let audioWithDescriptions = audibleGroup.options.first { option in
            option.hasMediaCharacteristic(.describesVideoForAccessibility)
        }

        // Select the audio description track if available
        if let descriptiveTrack = audioWithDescriptions {
            playerItem.select(descriptiveTrack, in: audibleGroup)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Advanced Player with Audio Track Selection&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct AdvancedAudioDescriptionPlayer: View {
    @State private var player: AVPlayer?
    @State private var audioTracks: [AudioTrackInfo] = []
    @State private var selectedTrack: AudioTrackInfo?
    @State private var showTrackPicker = false

    struct AudioTrackInfo: Identifiable, Hashable {
        let id = UUID()
        let displayName: String
        let hasDescriptions: Bool
        let option: AVMediaSelectionOption
    }

    var body: some View {
        VStack(spacing: 0) {
            if let player = player {
                VideoPlayer(player: player)
                    .frame(height: 300)
            }

            VStack(alignment: .leading, spacing: 16) {
                Text("Audio Options")
                    .font(.headline)

                if !audioTracks.isEmpty {
                    Button(action: { showTrackPicker.toggle() }) {
                        HStack {
                            VStack(alignment: .leading) {
                                Text(selectedTrack?.displayName ?? "Default")
                                    .font(.body)

                                if selectedTrack?.hasDescriptions == true {
                                    Text("Includes audio descriptions")
                                        .font(.caption)
                                        .foregroundColor(.green)
                                }
                            }

                            Spacer()

                            Image(systemName: "chevron.down")
                        }
                        .padding()
                        .background(Color.gray.opacity(0.1))
                        .cornerRadius(8)
                    }
                    .sheet(isPresented: $showTrackPicker) {
                        AudioTrackPicker(
                            tracks: audioTracks,
                            selectedTrack: $selectedTrack,
                            onSelect: { track in
                                selectAudioTrack(track)
                                showTrackPicker = false
                            }
                        )
                    }
                }
            }
            .padding()
        }
        .onAppear {
            setupPlayer()
        }
    }

    func setupPlayer() {
        guard let videoURL = Bundle.main.url(
            forResource: "sample",
            withExtension: "mp4"
        ) else { return }

        let asset = AVURLAsset(url: videoURL)
        let playerItem = AVPlayerItem(asset: asset)
        player = AVPlayer(playerItem: playerItem)

        loadAudioTracks(from: asset)

        // Auto-select audio descriptions if available
        if let trackWithDescriptions = audioTracks.first(where: { $0.hasDescriptions }) {
            selectedTrack = trackWithDescriptions
            selectAudioTrack(trackWithDescriptions)
        } else if let firstTrack = audioTracks.first {
            selectedTrack = firstTrack
        }
    }

    func loadAudioTracks(from asset: AVAsset) {
        guard let audibleGroup = asset.mediaSelectionGroup(
            forMediaCharacteristic: .audible
        ) else { return }

        audioTracks = audibleGroup.options.map { option in
            AudioTrackInfo(
                displayName: option.displayName,
                hasDescriptions: option.hasMediaCharacteristic(
                    .describesVideoForAccessibility
                ),
                option: option
            )
        }
    }

    func selectAudioTrack(_ track: AudioTrackInfo) {
        guard let playerItem = player?.currentItem,
              let audibleGroup = playerItem.asset.mediaSelectionGroup(
                forMediaCharacteristic: .audible
              ) else { return }

        playerItem.select(track.option, in: audibleGroup)
        selectedTrack = track
    }
}

struct AudioTrackPicker: View {
    let tracks: [AdvancedAudioDescriptionPlayer.AudioTrackInfo]
    @Binding var selectedTrack: AdvancedAudioDescriptionPlayer.AudioTrackInfo?
    let onSelect: (AdvancedAudioDescriptionPlayer.AudioTrackInfo) -&amp;gt; Void

    var body: some View {
        NavigationView {
            List(tracks) { track in
                Button(action: {
                    onSelect(track)
                }) {
                    HStack {
                        VStack(alignment: .leading) {
                            Text(track.displayName)
                                .font(.body)

                            if track.hasDescriptions {
                                Label("Audio descriptions", 
                                      systemImage: "speaker.wave.3")
                                    .font(.caption)
                                    .foregroundColor(.green)
                            }
                        }

                        Spacer()

                        if selectedTrack?.id == track.id {
                            Image(systemName: "checkmark")
                                .foregroundColor(.blue)
                        }
                    }
                }
                .foregroundColor(.primary)
            }
            .navigationTitle("Audio Track")
            .navigationBarTitleDisplayMode(.inline)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Creating Audio Description Tracks&lt;/h2&gt;
&lt;p&gt;Audio descriptions are typically created as separate audio tracks and mixed with the original audio. The process involves:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Script Writing&lt;/strong&gt;: Watch the video and write descriptions for visual elements during dialogue pauses&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Voice Recording&lt;/strong&gt;: Record the descriptions in a clear, professional voice&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Audio Mixing&lt;/strong&gt;: Mix the descriptions with the original audio&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Track Encoding&lt;/strong&gt;: Add the descriptive audio as an alternative track in your video file&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Adding Audio Description Track to Video&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import AVFoundation

func createVideoWithAudioDescriptions(
    videoURL: URL,
    standardAudioURL: URL,
    descriptiveAudioURL: URL,
    outputURL: URL,
    completion: @escaping (Bool) -&amp;gt; Void
) {
    let composition = AVMutableComposition()
    let videoAsset = AVURLAsset(url: videoURL)

    // Add video track
    guard let videoTrack = videoAsset.tracks(withMediaType: .video).first,
          let compositionVideoTrack = composition.addMutableTrack(
            withMediaType: .video,
            preferredTrackID: kCMPersistentTrackID_Invalid
          ) else {
        completion(false)
        return
    }

    do {
        try compositionVideoTrack.insertTimeRange(
            CMTimeRange(start: .zero, duration: videoAsset.duration),
            of: videoTrack,
            at: .zero
        )

        // Add standard audio track
        let standardAudio = AVURLAsset(url: standardAudioURL)
        if let standardTrack = standardAudio.tracks(withMediaType: .audio).first,
           let compositionAudioTrack = composition.addMutableTrack(
            withMediaType: .audio,
            preferredTrackID: kCMPersistentTrackID_Invalid
           ) {
            try compositionAudioTrack.insertTimeRange(
                CMTimeRange(start: .zero, duration: videoAsset.duration),
                of: standardTrack,
                at: .zero
            )
        }

        // Add descriptive audio track
        let descriptiveAudio = AVURLAsset(url: descriptiveAudioURL)
        if let descriptiveTrack = descriptiveAudio.tracks(withMediaType: .audio).first,
           let compositionDescriptiveTrack = composition.addMutableTrack(
            withMediaType: .audio,
            preferredTrackID: kCMPersistentTrackID_Invalid
           ) {
            try compositionDescriptiveTrack.insertTimeRange(
                CMTimeRange(start: .zero, duration: videoAsset.duration),
                of: descriptiveTrack,
                at: .zero
            )
        }

        // Export the composition
        guard let exportSession = AVAssetExportSession(
            asset: composition,
            presetName: AVAssetExportPresetHighestQuality
        ) else {
            completion(false)
            return
        }

        exportSession.outputURL = outputURL
        exportSession.outputFileType = .mp4

        exportSession.exportAsynchronously {
            completion(exportSession.status == .completed)
        }

    } catch {
        print("Error creating video with audio descriptions: \(error)")
        completion(false)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Best Practices for Audio Descriptions&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Describe What Matters&lt;/strong&gt;: Focus on visual information that's essential to understanding the content.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Use Natural Pauses&lt;/strong&gt;: Insert descriptions during natural breaks in dialogue to avoid overlapping with important audio.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Be Objective&lt;/strong&gt;: Describe what you see, not what you interpret. Say "She frowns" not "She looks angry."&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Be Concise&lt;/strong&gt;: Descriptions should be clear and brief, fitting naturally into available time.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Identify Speakers&lt;/strong&gt;: When new people appear on screen, describe them so users know who's speaking.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Describe Text&lt;/strong&gt;: Read important on-screen text like titles, signs, or messages.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Maintain Tone&lt;/strong&gt;: Match the style and mood of the content in your descriptions.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Provide Context&lt;/strong&gt;: Describe scene changes and settings so users understand where the action takes place.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Test with Users&lt;/strong&gt;: Have blind or low vision users review your descriptions for clarity and usefulness.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Automatic Selection of Audio Descriptions&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import AVFoundation

extension AVPlayerItem {
    func selectPreferredMediaOptions() {
        // Select audio descriptions if available
        if let audibleGroup = asset.mediaSelectionGroup(
            forMediaCharacteristic: .audible
        ) {
            let preferredOptions = AVMediaSelectionGroup.mediaSelectionOptions(
                from: audibleGroup.options,
                with: Locale.current
            )

            // Prioritize tracks with descriptions
            let trackWithDescriptions = preferredOptions.first { option in
                option.hasMediaCharacteristic(.describesVideoForAccessibility)
            }

            if let descriptiveTrack = trackWithDescriptions {
                select(descriptiveTrack, in: audibleGroup)
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;Audio descriptions are required if your app contains video with meaningful visual content. AVFoundation makes it manageable — include a separate audio track and select it automatically when the user has audio descriptions enabled.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/tag/Accessibility"&gt;More posts about Accessibility&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/accessibility-fundamentals"&gt;Accessibility Fundamentals&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/avfoundation/media_playback"&gt;AVFoundation Audio Programming Guide&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/mediaaccessibility"&gt;Media Accessibility Framework&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://www.w3.org/WAI/media/av/description/"&gt;Audio Description Best Practices&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/help/app-store-connect/manage-app-accessibility/audio-descriptions-evaluation-criteria"&gt;https://developer.apple.com/help/app-store-connect/manage-app-accessibility/audio-descriptions-evaluation-criteria&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Keyboard Navigation</title>    <link>https://wesleydegroot.nl/blog/keyboard-navigation</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/keyboard-navigation</guid>
    <pubDate>Mon, 30 Mar 2026 16:57:36 +0200</pubDate>
    <category>Accessibility</category>
    <category>SwiftUI</category>
    <description>&lt;p&gt;Keyboard navigation enables users to navigate and interact with your app using only a keyboard or assistive input devices. In this post, we'll explore how to implement comprehensive keyboard navigation support in SwiftUI, making your apps accessible to users with motor impairments, power users, and anyone who prefers keyboard-based interaction.&lt;/p&gt;
&lt;h2&gt;What is Keyboard Navigation?&lt;/h2&gt;
&lt;p&gt;Keyboard navigation allows users to navigate through an app using keyboard keys (Tab, arrow keys, Enter, etc.) or external assistive devices like switch controls, rather than touch or mouse input. This is essential for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Users with motor impairments&lt;/strong&gt; who have difficulty with precise touch or mouse movements&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Switch control users&lt;/strong&gt; who navigate using adaptive switches&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Power users&lt;/strong&gt; who prefer keyboard efficiency&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;iPad users with external keyboards&lt;/strong&gt; who want a desktop-like experience&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Users with tremors or limited dexterity&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Good keyboard navigation means users can access every interactive element, understand where focus is at all times, and complete all tasks without needing touch or mouse input.&lt;/p&gt;
&lt;h2&gt;Understanding Focus in SwiftUI&lt;/h2&gt;
&lt;p&gt;Focus determines which element receives keyboard input. SwiftUI provides several tools for managing focus:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;@FocusState&lt;/code&gt;: Property wrapper to track and control focus state&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;.focused(_:equals:)&lt;/code&gt;: Modifier to bind focus to a specific value&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;.focusable()&lt;/code&gt;: Makes non-standard elements focusable&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Automatic tab order: SwiftUI automatically creates a logical tab order&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Basic Focus Management&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct BasicFocusExample: View {
    enum Field: Hashable {
        case username
        case password
        case email
    }

    @FocusState private var focusedField: Field?
    @State private var username = ""
    @State private var password = ""
    @State private var email = ""

    var body: some View {
        Form {
            Section {
                TextField("Username", text: $username)
                    .focused($focusedField, equals: .username)
                    .textContentType(.username)

                SecureField("Password", text: $password)
                    .focused($focusedField, equals: .password)
                    .textContentType(.password)

                TextField("Email", text: $email)
                    .focused($focusedField, equals: .email)
                    .textContentType(.emailAddress)
                    .keyboardType(.emailAddress)
            }

            Section {
                Button("Sign Up") {
                    handleSignUp()
                }
            }
        }
        .onAppear {
            // Set initial focus
            focusedField = .username
        }
    }

    func handleSignUp() {
        // Validate and process
        if username.isEmpty {
            focusedField = .username
        } else if password.isEmpty {
            focusedField = .password
        } else if email.isEmpty {
            focusedField = .email
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Programmatic Focus Control&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct FocusControlExample: View {
    enum FormField: Hashable {
        case field1, field2, field3
    }

    @FocusState private var focusedField: FormField?
    @State private var field1Text = ""
    @State private var field2Text = ""
    @State private var field3Text = ""

    var body: some View {
        VStack(spacing: 20) {
            TextField("Field 1", text: $field1Text)
                .focused($focusedField, equals: .field1)
                .textFieldStyle(.roundedBorder)
                .onSubmit {
                    focusedField = .field2
                }

            TextField("Field 2", text: $field2Text)
                .focused($focusedField, equals: .field2)
                .textFieldStyle(.roundedBorder)
                .onSubmit {
                    focusedField = .field3
                }

            TextField("Field 3", text: $field3Text)
                .focused($focusedField, equals: .field3)
                .textFieldStyle(.roundedBorder)
                .onSubmit {
                    submitForm()
                }

            HStack(spacing: 16) {
                Button("Previous") {
                    moveFocusToPrevious()
                }
                .keyboardShortcut(.upArrow, modifiers: .command)

                Button("Next") {
                    moveFocusToNext()
                }
                .keyboardShortcut(.downArrow, modifiers: .command)

                Button("Submit") {
                    submitForm()
                }
                .keyboardShortcut(.return, modifiers: .command)
            }
        }
        .padding()
    }

    func moveFocusToPrevious() {
        switch focusedField {
        case .field2:
            focusedField = .field1
        case .field3:
            focusedField = .field2
        default:
            break
        }
    }

    func moveFocusToNext() {
        switch focusedField {
        case .field1:
            focusedField = .field2
        case .field2:
            focusedField = .field3
        default:
            break
        }
    }

    func submitForm() {
        print("Form submitted")
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Making Custom Views Focusable&lt;/h2&gt;
&lt;p&gt;Not all views are focusable by default. Use &lt;code&gt;.focusable()&lt;/code&gt; to make custom views keyboard-navigable:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct CustomButton: View {
    let title: String
    let action: () -&amp;gt; Void
    @FocusState private var isFocused: Bool

    var body: some View {
        Text(title)
            .padding()
            .background(isFocused ? Color.blue : Color.gray)
            .foregroundColor(.white)
            .cornerRadius(8)
            .focusable()
            .focused($isFocused)
            .onTapGesture {
                action()
            }
            .onKeyPress(.space) {
                action()
                return .handled
            }
            .onKeyPress(.return) {
                action()
                return .handled
            }
    }
}

struct CustomFocusableView: View {
    var body: some View {
        VStack(spacing: 20) {
            CustomButton(title: "First Button") {
                print("First tapped")
            }

            CustomButton(title: "Second Button") {
                print("Second tapped")
            }

            CustomButton(title: "Third Button") {
                print("Third tapped")
            }
        }
        .padding()
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Keyboard Shortcuts&lt;/h2&gt;
&lt;p&gt;Enhance keyboard navigation with shortcuts for common actions:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct KeyboardShortcutsExample: View {
    @State private var items: [String] = ["Item 1", "Item 2", "Item 3"]
    @State private var selectedItem: String?
    @State private var showingAddSheet = false

    var body: some View {
        NavigationView {
            List(items, id: \.self, selection: $selectedItem) { item in
                Text(item)
            }
            .navigationTitle("Items")
            .toolbar {
                ToolbarItemGroup {
                    Button(action: addItem) {
                        Label("Add", systemImage: "plus")
                    }
                    .keyboardShortcut("n", modifiers: .command)

                    Button(action: deleteSelectedItem) {
                        Label("Delete", systemImage: "trash")
                    }
                    .keyboardShortcut(.delete, modifiers: .command)
                    .disabled(selectedItem == nil)

                    Button(action: refreshItems) {
                        Label("Refresh", systemImage: "arrow.clockwise")
                    }
                    .keyboardShortcut("r", modifiers: .command)
                }
            }
        }
    }

    func addItem() {
        items.append("New Item \(items.count + 1)")
    }

    func deleteSelectedItem() {
        if let selected = selectedItem {
            items.removeAll { $0 == selected }
            selectedItem = nil
        }
    }

    func refreshItems() {
        print("Refreshing items...")
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Arrow Key Navigation in Custom Views&lt;/h2&gt;
&lt;p&gt;For custom collections or grids, implement arrow key navigation:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct GridKeyboardNavigation: View {
    @State private var selectedIndex: Int = 0
    let items = Array(0..&amp;lt;20)
    let columns = 4

    var body: some View {
        VStack {
            Text("Use arrow keys to navigate")
                .font(.headline)
                .padding()

            LazyVGrid(
                columns: Array(repeating: GridItem(.flexible()), count: columns),
                spacing: 16
            ) {
                ForEach(items, id: \.self) { index in
                    GridCell(
                        index: index,
                        isSelected: selectedIndex == index
                    )
                    .onTapGesture {
                        selectedIndex = index
                    }
                }
            }
            .padding()
            .focusable()
            .onKeyPress(.upArrow) {
                moveSelection(by: -columns)
                return .handled
            }
            .onKeyPress(.downArrow) {
                moveSelection(by: columns)
                return .handled
            }
            .onKeyPress(.leftArrow) {
                moveSelection(by: -1)
                return .handled
            }
            .onKeyPress(.rightArrow) {
                moveSelection(by: 1)
                return .handled
            }
        }
    }

    func moveSelection(by offset: Int) {
        let newIndex = selectedIndex + offset
        if newIndex &amp;gt;= 0 &amp;amp;&amp;amp; newIndex &amp;lt; items.count {
            selectedIndex = newIndex
        }
    }
}

struct GridCell: View {
    let index: Int
    let isSelected: Bool

    var body: some View {
        ZStack {
            RoundedRectangle(cornerRadius: 8)
                .fill(isSelected ? Color.blue : Color.gray.opacity(0.3))

            Text("\(index)")
                .foregroundColor(isSelected ? .white : .primary)
        }
        .frame(height: 80)
        .overlay(
            RoundedRectangle(cornerRadius: 8)
                .stroke(isSelected ? Color.blue : Color.clear, lineWidth: 3)
        )
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Focus Sections and Groups&lt;/h2&gt;
&lt;p&gt;Group related content to improve keyboard navigation flow:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct FocusedSectionsExample: View {
    enum FocusedSection: Hashable {
        case sidebar
        case content
        case details
    }

    @FocusState private var focusedSection: FocusedSection?
    @State private var selectedItem: String?

    var body: some View {
        HStack(spacing: 0) {
            // Sidebar
            VStack(alignment: .leading) {
                Text("Sidebar")
                    .font(.headline)
                    .padding()

                List {
                    ForEach(["Item 1", "Item 2", "Item 3"], id: \.self) { item in
                        Button(item) {
                            selectedItem = item
                        }
                    }
                }
            }
            .frame(width: 200)
            .background(Color.gray.opacity(0.1))
            .focusable()
            .focused($focusedSection, equals: .sidebar)

            Divider()

            // Main content
            VStack {
                Text("Main Content")
                    .font(.headline)
                    .padding()

                if let item = selectedItem {
                    Text("Selected: \(item)")
                        .padding()
                } else {
                    Text("No selection")
                        .foregroundColor(.secondary)
                        .padding()
                }
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .background(Color.white)
            .focusable()
            .focused($focusedSection, equals: .content)

            Divider()

            // Details panel
            VStack(alignment: .leading) {
                Text("Details")
                    .font(.headline)
                    .padding()

                Spacer()
            }
            .frame(width: 200)
            .background(Color.gray.opacity(0.05))
            .focusable()
            .focused($focusedSection, equals: .details)
        }
        .onKeyPress(.tab, modifiers: .command) {
            cycleFocusedSection()
            return .handled
        }
    }

    func cycleFocusedSection() {
        switch focusedSection {
        case .sidebar:
            focusedSection = .content
        case .content:
            focusedSection = .details
        case .details:
            focusedSection = .sidebar
        default:
            focusedSection = .sidebar
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Accessible Menus and Dropdowns&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct KeyboardAccessibleMenu: View {
    @State private var isMenuOpen = false
    @FocusState private var menuFocused: Bool

    var body: some View {
        VStack {
            Menu {
                Button(action: newDocument) {
                    Label("New", systemImage: "doc")
                }
                .keyboardShortcut("n", modifiers: .command)

                Button(action: openDocument) {
                    Label("Open", systemImage: "folder")
                }
                .keyboardShortcut("o", modifiers: .command)

                Divider()

                Button(action: saveDocument) {
                    Label("Save", systemImage: "square.and.arrow.down")
                }
                .keyboardShortcut("s", modifiers: .command)
            } label: {
                Label("File", systemImage: "doc.text")
            }
            .focused($menuFocused)
        }
        .padding()
    }

    func newDocument() { print("New") }
    func openDocument() { print("Open") }
    func saveDocument() { print("Save") }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Best Practices for Keyboard Navigation&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Logical Tab Order&lt;/strong&gt;: Ensure focus moves in a logical, predictable order (usually left-to-right, top-to-bottom).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Visible Focus Indicators&lt;/strong&gt;: Make it obvious which element has focus with clear visual styling.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Skip Links&lt;/strong&gt;: In complex layouts, provide shortcuts to jump to main content sections.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Keyboard Shortcuts&lt;/strong&gt;: Use standard keyboard shortcuts (⌘N for new, ⌘S for save, etc.) when applicable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Support All Interactions&lt;/strong&gt;: Everything possible with touch/mouse should be possible with keyboard.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Test Thoroughly&lt;/strong&gt;: Navigate your entire app using only the keyboard to find issues.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Preserve Context&lt;/strong&gt;: When opening/closing views, return focus to a logical location.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Handle Edge Cases&lt;/strong&gt;: Define behavior when reaching the first or last focusable element.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Document Shortcuts&lt;/strong&gt;: Provide a help screen or menu showing available keyboard shortcuts.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Testing Keyboard Navigation&lt;/h2&gt;
&lt;p&gt;To test keyboard navigation:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Connect an external keyboard to your iOS device or use the simulator&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use the Tab key to move between focusable elements&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use arrow keys for directional navigation&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Press Enter or Space to activate buttons&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Test all keyboard shortcuts&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Verify focus indicators are visible&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Ensure all functionality is accessible&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Common keys for navigation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Tab&lt;/strong&gt; / &lt;strong&gt;Shift+Tab&lt;/strong&gt;: Move forward/backward through focusable elements&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Arrow keys&lt;/strong&gt;: Navigate within a group or list&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Enter&lt;/strong&gt; / &lt;strong&gt;Space&lt;/strong&gt;: Activate focused button or control&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Escape&lt;/strong&gt;: Close dialogs or cancel actions&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Command+key&lt;/strong&gt;: Execute shortcuts&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;Keyboard navigation matters most for users with external keyboards and assistive input devices. SwiftUI's &lt;code&gt;@FocusState&lt;/code&gt; gives you the control to set logical tab order and handle keyboard shortcuts — test by unplugging your trackpad and navigating with Tab alone.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/blog/tag/Accessibility"&gt;More posts about Accessibility&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/accessibility-fundamentals"&gt;Accessibility Fundamentals&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/focusstate"&gt;SwiftUI Focus Management&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view-input-and-events"&gt;Keyboard Navigation in SwiftUI&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/uikit/uiaccessibility"&gt;UIAccessibility for Keyboard&lt;/a&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>withAnimation</title>    <link>https://wesleydegroot.nl/blog/withanimation</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/withanimation</guid>
    <pubDate>Mon, 30 Mar 2026 16:57:36 +0200</pubDate>
    <category>SwiftUI</category>
    <category>withAnimation</category>
    <description>&lt;p&gt;&lt;code&gt;withAnimation&lt;/code&gt; wraps state changes to produce smooth transitions. It's one of the most common ways to add animation to a SwiftUI view.&lt;/p&gt;
&lt;h2&gt;Use Case&lt;/h2&gt;
&lt;p&gt;A common use case for &lt;code&gt;withAnimation&lt;/code&gt; is when you want to animate the appearance or disappearance of a view based on a state change. For example, you might want to animate a button that toggles the visibility of a text label.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI  
struct ContentView: View {
    @State private var isVisible = false

    var body: some View {
        VStack {
            Button("Toggle Text") {
                withAnimation {
                    isVisible.toggle()
                }
            }

            if isVisible {
                Text("Hello, SwiftUI!")
                    .transition(.slide)
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, when the button is pressed, the &lt;code&gt;isVisible&lt;/code&gt; state toggles within a &lt;code&gt;withAnimation&lt;/code&gt; block, causing the text to slide in and out smoothly.&lt;/p&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;p&gt;While &lt;code&gt;withAnimation&lt;/code&gt; is powerful, there are a few caveats to keep in mind:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Performance&lt;/strong&gt;: Overusing animations can lead to performance issues, especially on older devices. Use animations judiciously to ensure a smooth user experience.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;State Changes&lt;/strong&gt;: Ensure that the state changes you want to animate are directly related to the views being animated. If the state change does not affect the view hierarchy, the animation may not work as expected.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Animation Types&lt;/strong&gt;: Not all view changes can be animated. Some properties may not support animation, so be sure to test your animations thoroughly. &lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Accessibility&lt;/h2&gt;
&lt;p&gt;When using animations, it's important to consider accessibility. Some users may have motion sensitivity and prefer reduced motion settings. SwiftUI respects the user's system preferences for reduced motion, so animations will be disabled if the user has enabled this setting in their device's accessibility options.&lt;/p&gt;
&lt;p&gt;See &lt;a href="/blog/reduced-motion"&gt;Respecting Reduced Motion&lt;/a&gt; for more information.&lt;/p&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;withAnimation&lt;/code&gt; is the simplest way to add animation to a SwiftUI app. Keep reduced motion in mind — check out the &lt;a href="/blog/reduced-motion"&gt;Reduced Motion&lt;/a&gt; post for how to handle that properly.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/withanimation%28_%3A_%3A%29"&gt;withanimation(&lt;em&gt;:&lt;/em&gt;:)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Focus State in SwiftUI</title>    <link>https://wesleydegroot.nl/blog/focus-state-in-swiftui</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/focus-state-in-swiftui</guid>
    <pubDate>Mon, 30 Mar 2026 16:57:37 +0200</pubDate>
    <category>SwiftUI</category>
    <category>Accessibility</category>
    <description>&lt;p&gt;Managing focus state in SwiftUI is essential for creating accessible and user-friendly forms and interactive interfaces. The &lt;code&gt;@FocusState&lt;/code&gt; property wrapper allows you to programmatically control which field has focus, improving keyboard navigation and user experience.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;@FocusState&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;@FocusState&lt;/code&gt; is a property wrapper introduced in iOS 15 that enables you to track and control which view has keyboard focus. This is particularly useful for forms, text fields, and creating accessible user interfaces where keyboard navigation is important.&lt;/p&gt;
&lt;h2&gt;Basic Usage for a search view&lt;/h2&gt;
&lt;p&gt;Here's an example of using &lt;code&gt;@FocusState&lt;/code&gt; in a search view:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct SearchView: View {
    @State private var searchText = ""
    @FocusState private var isSearchFieldFocused: Bool

    var body: some View {
        VStack {
            TextField("Search...", text: $searchText)
                .focused($isSearchFieldFocused)
        }
        .padding()
        .task {
            isSearchFieldFocused = true // Automatically focus the search field when the view appears
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Basic Usage (User Login Form)&lt;/h2&gt;
&lt;p&gt;Here's a simple example of using &lt;code&gt;@FocusState&lt;/code&gt; with a text field:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct LoginView: View {
    @State private var username = ""
    @State private var password = ""
    @FocusState private var focusedField: Field?

    enum Field {
        case username
        case password
    }

    var body: some View {
        Form {
            TextField("Username", text: $username)
                .focused($focusedField, equals: .username)

            SecureField("Password", text: $password)
                .focused($focusedField, equals: .password)

            Button("Login") {
                focusedField = nil // Dismiss keyboard
            }
        }
        .onAppear {
            focusedField = .username
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Use Case: Form Validation (Sign-Up Form)&lt;/h2&gt;
&lt;p&gt;You can use &lt;code&gt;@FocusState&lt;/code&gt; to automatically move focus to the next field after validation or to highlight invalid fields:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct SignUpForm: View {
    @State private var email = ""
    @State private var password = ""
    @State private var confirmPassword = ""
    @FocusState private var focusedField: Field?

    enum Field: Hashable {
        case email, password, confirmPassword
    }

    var body: some View {
        Form {
            TextField("Email", text: $email)
                .focused($focusedField, equals: .email)
                .onSubmit {
                    focusedField = .password
                }

            SecureField("Password", text: $password)
                .focused($focusedField, equals: .password)
                .onSubmit {
                    focusedField = .confirmPassword
                }

            SecureField("Confirm Password", text: $confirmPassword)
                .focused($focusedField, equals: .confirmPassword)
                .onSubmit {
                    submitForm()
                }
        }
    }

    func submitForm() {
        guard !email.isEmpty else {
            focusedField = .email
            return
        }
        guard password == confirmPassword else {
            focusedField = .confirmPassword
            return
        }
        focusedField = nil
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Accessibility Benefits&lt;/h2&gt;
&lt;p&gt;Using &lt;code&gt;@FocusState&lt;/code&gt; improves accessibility by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Enabling better keyboard navigation&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Supporting assistive technologies&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Providing clear focus indicators&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Allowing programmatic focus control for better UX&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;@FocusState&lt;/code&gt; is a powerful tool for managing keyboard focus in SwiftUI applications. It enhances both the user experience and accessibility of your forms and interactive views by providing fine-grained control over focus management.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/focusstate"&gt;FocusState&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/focused(_:equals:)"&gt;focused(_:equals:)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>TextField Styles in SwiftUI</title>    <link>https://wesleydegroot.nl/blog/textfield-styles-in-swiftui</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/textfield-styles-in-swiftui</guid>
    <pubDate>Mon, 30 Mar 2026 16:57:37 +0200</pubDate>
    <category>SwiftUI</category>
    <category>UI</category>
    <description>&lt;p&gt;SwiftUI provides various built-in text field styles that allow you to customize the appearance of text input fields. Understanding these styles helps you create consistent and polished user interfaces that match your app's design language.&lt;/p&gt;
&lt;h2&gt;What are TextField Styles?&lt;/h2&gt;
&lt;p&gt;TextField styles in SwiftUI are modifiers that change the visual appearance of text fields. SwiftUI includes several built-in styles like &lt;code&gt;.automatic&lt;/code&gt;, &lt;code&gt;.plain&lt;/code&gt;, and &lt;code&gt;.roundedBorder&lt;/code&gt;, and you can also create custom styles using the &lt;code&gt;TextFieldStyle&lt;/code&gt; protocol.&lt;/p&gt;
&lt;h2&gt;Built-in TextField Styles&lt;/h2&gt;
&lt;p&gt;Here are the main built-in text field styles:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct TextFieldStylesView: View {
    @State private var text1 = ""
    @State private var text2 = ""
    @State private var text3 = ""

    var body: some View {
        Form {
            Section("Automatic Style") {
                TextField("Enter text", text: $text1)
                    .textFieldStyle(.automatic)
            }

            Section("Plain Style") {
                TextField("Enter text", text: $text2)
                    .textFieldStyle(.plain)
            }

            Section("Rounded Border Style") {
                TextField("Enter text", text: $text3)
                    .textFieldStyle(.roundedBorder)
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Creating Custom TextField Styles&lt;/h2&gt;
&lt;p&gt;You can create your own custom text field styles by conforming to the &lt;code&gt;TextFieldStyle&lt;/code&gt; protocol:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct CustomTextFieldStyle: TextFieldStyle {
    func _body(configuration: TextField&amp;lt;Self._Label&amp;gt;) -&amp;gt; some View {
        configuration
            .padding()
            .background(
                RoundedRectangle(cornerRadius: 10)
                    .fill(Color.blue.opacity(0.1))
            )
            .overlay(
                RoundedRectangle(cornerRadius: 10)
                    .stroke(Color.blue, lineWidth: 2)
            )
    }
}

struct ContentView: View {
    @State private var text = ""

    var body: some View {
        TextField("Custom styled field", text: $text)
            .textFieldStyle(CustomTextFieldStyle())
            .padding()
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Platform-Specific Considerations&lt;/h2&gt;
&lt;p&gt;Different platforms handle text field styles differently:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;On iOS, &lt;code&gt;.automatic&lt;/code&gt; typically provides an underlined style in forms&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;On macOS, the default style is plain with a focus ring&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The &lt;code&gt;.roundedBorder&lt;/code&gt; style provides the familiar rounded rectangle border&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Accessibility&lt;/h2&gt;
&lt;p&gt;When customizing text field styles, ensure that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Focus indicators remain visible for keyboard navigation&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Color contrast meets accessibility guidelines&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Touch targets are appropriately sized (at least 44x44 points)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;TextField styles in SwiftUI cover most needs out of the box. If you need something more custom, &lt;code&gt;TextFieldStyle&lt;/code&gt; is straightforward to implement.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/textfieldstyle"&gt;TextFieldStyle&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/textfieldstyle(_:)"&gt;textFieldStyle(_:)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>AsyncImage in SwiftUI</title>    <link>https://wesleydegroot.nl/blog/asyncimage-in-swiftui</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/asyncimage-in-swiftui</guid>
    <pubDate>Sun, 05 Apr 2026 20:18:13 +0200</pubDate>
    <category>SwiftUI</category>
    <category>Networking</category>
    <description>&lt;p&gt;AsyncImage is a SwiftUI view that simplifies loading and displaying images from remote URLs. It handles the asynchronous nature of network requests, provides loading states, and manages caching automatically.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;AsyncImage&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;AsyncImage&lt;/code&gt; is a SwiftUI view introduced in iOS 15 that loads and displays images from a URL asynchronously. It eliminates the need for manual URLSession code and state management when displaying remote images.&lt;/p&gt;
&lt;h2&gt;Basic Usage&lt;/h2&gt;
&lt;p&gt;Here's a simple example of using AsyncImage:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct ImageView: View {
    var body: some View {
        AsyncImage(url: URL(string: "https://wesleydegroot.nl/assets/avatar/avatar.png"))
            .frame(width: 200, height: 200)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Handling Different States&lt;/h2&gt;
&lt;p&gt;You can customize the appearance during different loading phases:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct EnhancedImageView: View {
    let imageURL = URL(string: "https://wesleydegroot.nl/assets/avatar/avatar.png")

    var body: some View {
        AsyncImage(url: imageURL) { phase in
            switch phase {
            case .empty:
                ProgressView()
            case .success(let image):
                image
                    .resizable()
                    .aspectRatio(contentMode: .fit)
            case .failure:
                Image(systemName: "photo")
                    .foregroundColor(.gray)
            @unknown default:
                EmptyView()
            }
        }
        .frame(width: 200, height: 200)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Use Case: Image Gallery&lt;/h2&gt;
&lt;p&gt;Here's an example of using AsyncImage in a grid layout:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct ImageGalleryView: View {
    let imageUrls = [
        "https://wesleydegroot.nl/assets/avatar/avatar_401.png",
        "https://wesleydegroot.nl/assets/avatar/avatar_402.png",
        "https://wesleydegroot.nl/assets/avatar/avatar_403.png",
        "https://wesleydegroot.nl/assets/avatar/avatar_404.png"
    ]

    var body: some View {
        LazyVGrid(columns: [GridItem(.adaptive(minimum: 100))]) {
            ForEach(imageUrls, id: \.self) { urlString in
                AsyncImage(url: URL(string: urlString)) { image in
                    image
                        .resizable()
                        .aspectRatio(contentMode: .fill)
                } placeholder: {
                    Color.gray.opacity(0.3)
                }
                .frame(width: 100, height: 100)
                .clipped()
                .cornerRadius(8)
            }
        }
        .padding()
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/asyncimage"&gt;AsyncImage&lt;/a&gt; does not provide advanced caching options&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For more control, consider using third-party libraries or building a custom solution&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Images are cached automatically by URLSession, but cache control is limited&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For better performance with many images, consider using CachedAsyncImage or similar libraries&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Alternatives&lt;/h2&gt;
&lt;p&gt;For more advanced image loading features, check out:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/0xWDG/CachedAsyncImage"&gt;CachedAsyncImage&lt;/a&gt; - My package for enhanced async image loading with better caching&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/SDWebImage/SDWebImageSwiftUI"&gt;SDWebImageSwiftUI&lt;/a&gt; - Popular third-party library&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/onevcat/Kingfisher"&gt;Kingfisher&lt;/a&gt; - Another popular option with extensive features&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/asyncimage"&gt;AsyncImage&lt;/a&gt; is a convenient built-in solution for loading remote images in SwiftUI. While it works well for basic use cases, consider alternatives for apps that require advanced caching or loading features.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/asyncimage"&gt;AsyncImage&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/0xWDG/CachedAsyncImage"&gt;https://github.com/0xWDG/CachedAsyncImage&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Searchable Modifier in SwiftUI</title>    <link>https://wesleydegroot.nl/blog/searchable-modifier-in-swiftui</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/searchable-modifier-in-swiftui</guid>
    <pubDate>Mon, 13 Apr 2026 16:31:49 +0200</pubDate>
    <category>SwiftUI</category>
    <category>Search</category>
    <description>&lt;p&gt;The searchable modifier in SwiftUI makes it easy to add search functionality to your views. It provides a native search experience with automatic keyboard handling, search suggestions, and integration with the navigation bar.&lt;/p&gt;
&lt;h2&gt;What is the &lt;code&gt;searchable&lt;/code&gt; Modifier?&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;searchable&lt;/code&gt; modifier, introduced in iOS 15, adds a search field to your view. It integrates seamlessly with NavigationStack and List views, providing a familiar search experience that users expect.&lt;/p&gt;
&lt;h2&gt;Basic Usage&lt;/h2&gt;
&lt;p&gt;Here's a simple example of adding search to a list:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct SearchableListView: View {
    @State private var searchText = ""
    let items = ["Apple", "Banana", "Cherry", "Date", "Elderberry"]

    var filteredItems: [String] {
        if searchText.isEmpty {
            return items
        }
        return items.filter { $0.localizedCaseInsensitiveContains(searchText) }
    }

    var body: some View {
        NavigationStack {
            List(filteredItems, id: \.self) { item in
                Text(item)
            }
            .navigationTitle("Fruits")
            .searchable(text: $searchText, prompt: "Search fruits")
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Search Suggestions&lt;/h2&gt;
&lt;p&gt;You can provide search suggestions that appear as the user types:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct SearchWithSuggestionsView: View {
    @State private var searchText = ""
    let allItems = ["Swift", "SwiftUI", "UIKit", "Combine", "CoreData"]

    var searchResults: [String] {
        if searchText.isEmpty {
            return allItems
        }
        return allItems.filter { $0.localizedCaseInsensitiveContains(searchText) }
    }

    var body: some View {
        NavigationStack {
            List(searchResults, id: \.self) { item in
                Text(item)
            }
            .navigationTitle("Technologies")
            .searchable(text: $searchText) {
                ForEach(popularSearches, id: \.self) { suggestion in
                    Text(suggestion)
                        .searchCompletion(suggestion)
                }
            }
        }
    }

    var popularSearches: [String] {
        ["Swift", "SwiftUI", "Combine"]
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Search Scopes&lt;/h2&gt;
&lt;p&gt;You can add search scopes to filter results by category:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct SearchWithScopesView: View {
    @State private var searchText = ""
    @State private var searchScope: SearchScope = .all

    enum SearchScope: String, CaseIterable {
        case all = "All"
        case recent = "Recent"
        case favorites = "Favorites"
    }

    var body: some View {
        NavigationStack {
            List {
                // Your content here
            }
            .navigationTitle("Search")
            .searchable(text: $searchText)
            .searchScopes($searchScope) {
                ForEach(SearchScope.allCases, id: \.self) { scope in
                    Text(scope.rawValue)
                }
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Accessibility&lt;/h2&gt;
&lt;p&gt;The searchable modifier provides good accessibility support out of the box:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;VoiceOver announces the search field&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Keyboard navigation works automatically&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Search is dismissible with the cancel button&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;.searchable&lt;/code&gt; modifier is the quickest way to add search to a SwiftUI app. Scopes and suggestions are opt-in, so start simple and add them only if your content needs them.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/searchable(text:placement:prompt:)"&gt;searchable(text:placement:prompt:)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view/searchcompletion(_:)"&gt;searchCompletion(_:)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>ScrollView Performance in SwiftUI</title>    <link>https://wesleydegroot.nl/blog/scrollview-performance-in-swiftui</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/scrollview-performance-in-swiftui</guid>
    <pubDate>Mon, 30 Mar 2026 16:57:38 +0200</pubDate>
    <category>SwiftUI</category>
    <category>Performance</category>
    <description>&lt;p&gt;Slow scroll views are one of the most noticeable performance problems in SwiftUI apps. Optimizing them can make a significant difference, especially when dealing with large datasets or complex layouts.&lt;/p&gt;
&lt;h2&gt;Understanding ScrollView Performance&lt;/h2&gt;
&lt;p&gt;ScrollView in SwiftUI doesn't have built-in view recycling like UITableView or UICollectionView. This means all views are created upfront, which can lead to performance issues with large datasets. However, LazyVStack and LazyHStack provide lazy loading capabilities.&lt;/p&gt;
&lt;h2&gt;Use LazyVStack and LazyHStack&lt;/h2&gt;
&lt;p&gt;For better performance, use lazy stacks instead of regular ones inside ScrollView:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct PerformantScrollView: View {
    let items = Array(1...1000)

    var body: some View {
        ScrollView {
            // Good: LazyVStack creates views on demand
            LazyVStack {
                ForEach(items, id: \.self) { item in
                    ItemRow(number: item)
                }
            }
        }
    }
}

struct ItemRow: View {
    let number: Int

    var body: some View {
        Text("Item \(number)")
            .frame(height: 50)
            .frame(maxWidth: .infinity)
            .background(Color.blue.opacity(0.1))
            .cornerRadius(8)
            .padding(.horizontal)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Avoid Heavy Computations in Views&lt;/h2&gt;
&lt;p&gt;Move expensive operations outside the view body:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct OptimizedItemView: View {
    let item: Item
    let processedData: String // Computed outside the view

    var body: some View {
        VStack {
            Text(item.title)
            Text(processedData)
        }
    }
}

// Instead of:
// var body: some View {
//     Text(expensiveComputation()) // Called on every render — avoid this
// }&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Use LazyVGrid and LazyHGrid for Grids&lt;/h2&gt;
&lt;p&gt;For grid layouts, use lazy grids:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct GridView: View {
    let items = Array(1...100)
    let columns = [
        GridItem(.adaptive(minimum: 100))
    ]

    var body: some View {
        ScrollView {
            LazyVGrid(columns: columns) {
                ForEach(items, id: \.self) { item in
                    RoundedRectangle(cornerRadius: 10)
                        .fill(Color.blue)
                        .frame(height: 100)
                        .overlay(Text("\(item)"))
                }
            }
            .padding()
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Optimize Image Loading&lt;/h2&gt;
&lt;p&gt;Use AsyncImage or cached image loading for remote images:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct ImageListView: View {
    let imageUrls: [URL]

    var body: some View {
        ScrollView {
            LazyVStack {
                ForEach(imageUrls, id: \.self) { url in
                    AsyncImage(url: url) { image in
                        image.resizable()
                    } placeholder: {
                        Color.gray
                    }
                    .frame(height: 200)
                }
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Monitor Performance&lt;/h2&gt;
&lt;p&gt;Use Instruments to profile your scroll views:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Time Profiler to identify slow code&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;SwiftUI Profiler to see view updates&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Memory profiler to check for leaks&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;LazyVStack/LazyHStack don't support all modifiers the same way as regular stacks&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Be careful with onAppear/onDisappear in lazy stacks as they're called when views enter/exit viewport&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Complex view hierarchies still impact performance even with lazy loading&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;Optimizing ScrollView performance in SwiftUI involves using lazy stacks, minimizing expensive computations, and being mindful of view creation. These techniques will help you build smooth, responsive scrolling experiences.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/lazyvstack"&gt;LazyVStack&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/lazyhstack"&gt;LazyHStack&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/lazyvgrid"&gt;LazyVGrid&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>Custom Shapes in SwiftUI</title>    <link>https://wesleydegroot.nl/blog/custom-shapes-in-swiftui</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/custom-shapes-in-swiftui</guid>
    <pubDate>Mon, 30 Mar 2026 16:57:38 +0200</pubDate>
    <category>SwiftUI</category>
    <category>Graphics</category>
    <description>&lt;p&gt;Creating custom shapes in SwiftUI allows you to draw unique graphics and build creative user interfaces. By conforming to the Shape protocol, you can create reusable, scalable vector graphics that integrate seamlessly with SwiftUI's rendering system.&lt;/p&gt;
&lt;h2&gt;What are Custom Shapes?&lt;/h2&gt;
&lt;p&gt;Custom shapes in SwiftUI are types that conform to the &lt;code&gt;Shape&lt;/code&gt; protocol. They define a path that SwiftUI can fill, stroke, or use for clipping. Shapes are resolution-independent and scale beautifully.&lt;/p&gt;
&lt;h2&gt;Creating a Basic Custom Shape&lt;/h2&gt;
&lt;p&gt;Here's how to create a simple triangle shape:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct Triangle: Shape {
    func path(in rect: CGRect) -&amp;gt; Path {
        var path = Path()

        path.move(to: CGPoint(x: rect.midX, y: rect.minY))
        path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
        path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
        path.closeSubpath()

        return path
    }
}

struct ContentView: View {
    var body: some View {
        Triangle()
            .fill(Color.blue)
            .frame(width: 200, height: 200)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Advanced Custom Shape: Star&lt;/h2&gt;
&lt;p&gt;Here's a more complex example creating a star shape:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct Star: Shape {
    let points: Int
    let smoothness: Double

    func path(in rect: CGRect) -&amp;gt; Path {
        let center = CGPoint(x: rect.width / 2, y: rect.height / 2)
        let outerRadius = min(rect.width, rect.height) / 2
        let innerRadius = outerRadius * smoothness
        var angle: Double = -.pi / 2
        let angleIncrement = .pi * 2 / Double(points * 2)

        var path = Path()

        for point in 0..&amp;lt;points * 2 {
            let radius = point.isMultiple(of: 2) ? outerRadius : innerRadius
            let x = center.x + CGFloat(cos(angle)) * radius
            let y = center.y + CGFloat(sin(angle)) * radius

            if point == 0 {
                path.move(to: CGPoint(x: x, y: y))
            } else {
                path.addLine(to: CGPoint(x: x, y: y))
            }

            angle += angleIncrement
        }

        path.closeSubpath()
        return path
    }
}

struct StarView: View {
    var body: some View {
        Star(points: 5, smoothness: 0.45)
            .fill(Color.yellow)
            .frame(width: 200, height: 200)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Animatable Shapes&lt;/h2&gt;
&lt;p&gt;You can make shapes animatable by conforming to &lt;code&gt;Animatable&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct AnimatableStar: Shape {
    var points: Double

    var animatableData: Double {
        get { points }
        set { points = newValue }
    }

    func path(in rect: CGRect) -&amp;gt; Path {
        Star(points: Int(points), smoothness: 0.45).path(in: rect)
    }
}

struct AnimatedStarView: View {
    @State private var points: Double = 5

    var body: some View {
        VStack {
            AnimatableStar(points: points)
                .fill(Color.blue)
                .frame(width: 200, height: 200)
                .animation(.easeInOut, value: points)

            Slider(value: $points, in: 3...10, step: 1)
        }
        .padding()
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Use Cases&lt;/h2&gt;
&lt;p&gt;Custom shapes are perfect for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Creating unique UI elements&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Building custom charts and graphs&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Designing logos and icons&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Creating progress indicators&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Building custom buttons and controls&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;Shape&lt;/code&gt; protocol is straightforward — implement &lt;code&gt;path(in:)&lt;/code&gt; and SwiftUI handles the rest. Custom shapes scale infinitely and work with any fill, stroke, or trim modifier.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/shape"&gt;Shape&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/path"&gt;Path&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
  <item>
    <title>GeometryReader in SwiftUI</title>    <link>https://wesleydegroot.nl/blog/geometryreader-in-swiftui</link>
    <guid isPermaLink="true">https://wesleydegroot.nl/blog/geometryreader-in-swiftui</guid>
    <pubDate>Mon, 30 Mar 2026 16:57:39 +0200</pubDate>
    <category>SwiftUI</category>
    <category>Layout</category>
    <description>&lt;p&gt;GeometryReader is a SwiftUI view that provides access to the size and position of its parent container. It's essential for creating responsive layouts, custom alignments, and size-dependent views.&lt;/p&gt;
&lt;h2&gt;What is &lt;code&gt;GeometryReader&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;GeometryReader&lt;/code&gt; is a container view that makes its child view aware of its size and coordinate space. It passes a &lt;code&gt;GeometryProxy&lt;/code&gt; to its content closure, which you can use to query dimensional information.&lt;/p&gt;
&lt;h2&gt;Basic Usage&lt;/h2&gt;
&lt;p&gt;Here's a simple example of using GeometryReader:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;import SwiftUI

struct GeometryExample: View {
    var body: some View {
        GeometryReader { geometry in
            VStack {
                Text("Width: \(geometry.size.width)")
                Text("Height: \(geometry.size.height)")
            }
            .frame(width: geometry.size.width, height: geometry.size.height)
            .background(Color.blue.opacity(0.3))
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Creating Responsive Layouts&lt;/h2&gt;
&lt;p&gt;Use GeometryReader to create layouts that adapt to available space:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct ResponsiveGrid: View {
    var body: some View {
        GeometryReader { geometry in
            let columns = Int(geometry.size.width / 100)
            let spacing: CGFloat = 10
            let itemWidth = (geometry.size.width - (CGFloat(columns + 1) * spacing)) / CGFloat(columns)

            ScrollView {
                LazyVGrid(
                    columns: Array(repeating: GridItem(.fixed(itemWidth), spacing: spacing), count: columns),
                    spacing: spacing
                ) {
                    ForEach(1...50, id: \.self) { item in
                        RoundedRectangle(cornerRadius: 8)
                            .fill(Color.blue)
                            .frame(height: itemWidth)
                            .overlay(Text("\(item)"))
                    }
                }
                .padding(spacing)
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Coordinate Spaces&lt;/h2&gt;
&lt;p&gt;GeometryReader can convert coordinates between different coordinate spaces:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct CoordinateExample: View {
    var body: some View {
        GeometryReader { geometry in
            ScrollView {
                ForEach(0..&amp;lt;20) { index in
                    GeometryReader { itemGeometry in
                        let minY = itemGeometry.frame(in: .global).minY
                        let screenHeight = geometry.size.height
                        let progress = (screenHeight - minY) / screenHeight

                        Text("Item \(index)")
                            .frame(height: 100)
                            .frame(maxWidth: .infinity)
                            .background(
                                Color.blue.opacity(Double(progress))
                            )
                    }
                    .frame(height: 100)
                }
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Creating Circular Progress View&lt;/h2&gt;
&lt;p&gt;Here's a practical example using GeometryReader:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-swift"&gt;struct CircularProgressView: View {
    let progress: Double

    var body: some View {
        GeometryReader { geometry in
            ZStack {
                Circle()
                    .stroke(Color.gray.opacity(0.3), lineWidth: 20)

                Circle()
                    .trim(from: 0, to: progress)
                    .stroke(Color.blue, style: StrokeStyle(lineWidth: 20, lineCap: .round))
                    .rotationEffect(.degrees(-90))

                Text("\(Int(progress * 100))%")
                    .font(.system(size: geometry.size.width * 0.3, weight: .bold))
            }
        }
        .aspectRatio(1, contentMode: .fit)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;GeometryReader takes all available space by default&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Can cause layout issues if not properly constrained&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Overusing GeometryReader can impact performance&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Consider using layout protocols (iOS 16+) for some use cases&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;GeometryReader is a powerful tool for creating responsive and adaptive layouts in SwiftUI. While it should be used judiciously, it's essential for scenarios where you need to know the size and position of views.&lt;/p&gt;
&lt;p&gt;Resources: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/geometryreader"&gt;GeometryReader&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/geometryproxy"&gt;GeometryProxy&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
  </item>
    </channel>
</rss>