{"id":49860,"date":"2025-10-31T13:27:32","date_gmt":"2025-10-31T17:27:32","guid":{"rendered":"https:\/\/mjtsai.com\/blog\/?p=49860"},"modified":"2025-10-31T13:27:32","modified_gmt":"2025-10-31T17:27:32","slug":"swift-6-2-observations","status":"publish","type":"post","link":"https:\/\/mjtsai.com\/blog\/2025\/10\/31\/swift-6-2-observations\/","title":{"rendered":"Swift 6.2: Observations"},"content":{"rendered":"<p><a href=\"https:\/\/www.swift.org\/blog\/swift-6.2-released\/\">Holly Borla<\/a>:<\/p>\n<blockquote cite=\"https:\/\/www.swift.org\/blog\/swift-6.2-released\/\"><p>Swift 6.2 enables streaming transactional state changes of observable types using the new <a href=\"https:\/\/developer.apple.com\/documentation\/observation\/observations\"><code>Observations<\/code><\/a> async sequence type. Updates include all synchronous changes to the observable properties, and the transaction ends at the next <code>await<\/code> that suspends. This avoids redundant UI updates, improves performance, and ensures that your code reacts to a consistent snapshot of the value.<\/p><\/blockquote>\n\n<p>As with <a href=\"https:\/\/mjtsai.com\/blog\/2025\/10\/29\/swift-6-2-notificationcenter-messages\/\">notification center messages<\/a>, this is really part of the macOS 26 frameworks rather than Swift 6.2 itself.<\/p>\n\n<p><a href=\"https:\/\/www.donnywals.com\/using-observations-to-observe-observable-model-properties\/\">Donny Wals<\/a>:<\/p>\n<blockquote cite=\"https:\/\/www.donnywals.com\/using-observations-to-observe-observable-model-properties\/\"><p>Sequences created by <code>Observations<\/code> will automatically observe all properties that you accessed in your <code>Observations<\/code> closure. In this case we&rsquo;ve only accessed a single property so we&rsquo;re informed whenever <code>count<\/code> is changed. If we accessed more properties, a change to any of the accessed properties will cause us to receive a new value. Whatever we return from <code>Observations<\/code> is what our async sequence will output. In this case that&rsquo;s a string but it can be anything we want. The properties we access don&rsquo;t have to be part of our return value. Accessing the property is enough to have your closure called, even when you don&rsquo;t use that property to compute your return value.<\/p><p>[&#8230;]<\/p><p>When iterating over our <code>Observations<\/code> sequence we&rsquo;ll receive values in our loop <em>after<\/em> they&rsquo;ve been assigned to our <code>@Observable<\/code> model. This means that <code>Observations<\/code> sequences have &ldquo;did set semantics&rdquo; while <a href=\"https:\/\/www.donnywals.com\/observing-properties-on-an-observable-class-outside-of-swiftui-views\/\"><code>withObservationTracking<\/code><\/a> would have given us &ldquo;will set semantics&rdquo;.<\/p><\/blockquote>\n\n<p><a href=\"https:\/\/useyourloaf.com\/blog\/swift-observations-asyncsequence-for-state-changes\/\">Keith Harrison<\/a>:<\/p>\n<blockquote cite=\"https:\/\/useyourloaf.com\/blog\/swift-observations-asyncsequence-for-state-changes\/\">\n<p>The updates are transactional so multiple synchronous changes arrive as a single updated value.<\/p>\n<\/blockquote>\n\n<p><a href=\"https:\/\/github.com\/swiftlang\/swift-evolution\/blob\/main\/proposals\/0475-observed.md\">SE-0475<\/a>:<\/p>\n<blockquote cite=\"https:\/\/github.com\/swiftlang\/swift-evolution\/blob\/main\/proposals\/0475-observed.md\">\n<p>Observation was introduced to add the ability to observe changes in graphs of objects. The initial tools for observation afforded seamless integration into SwiftUI, however aiding SwiftUI is not the only intent of the module - it is more general than that. This proposal describes a new safe, ergonomic and composable way to observe changes to models using an AsyncSequence, starting transactions at the first willSet and then emitting a value upon that transaction end at the first point of consistency by interoperating with Swift Concurrency.<\/p>\n<p>[&#8230;]<\/p>\n<p>This proposal does not change the fact that the spectrum of APIs may range from\nfavoring <code>AsyncSequence<\/code> properties to purely <code>@Observable<\/code> models. They both\nhave their place. However the calculus of determining the best exposition may\nbe slightly more refined now with <code>Observations<\/code>.<\/p>\n<\/blockquote>\n\n<p><a href=\"https:\/\/jaredsinclair.com\/2025\/09\/10\/observation.html\">Jared Sinclair<\/a>:<\/p>\n<blockquote cite=\"https:\/\/jaredsinclair.com\/2025\/09\/10\/observation.html\"><p>Apple has effectively deprecated the reigning paradigm of the <a href=\"https:\/\/developer.apple.com\/documentation\/combine\/observableobject\">ObservableObject<\/a> protocol and <a href=\"https:\/\/developer.apple.com\/documentation\/combine\/published\">@Published<\/a> properties observed via the Combine framework, but they&rsquo;ve only partially provided its replacement via the <a href=\"https:\/\/developer.apple.com\/documentation\/observation\/observable()\">@Observable<\/a> macro and the <a href=\"https:\/\/developer.apple.com\/documentation\/observation\/withobservationtracking(_:onchange:)\">withObservationTracking<\/a> free function. The gaps between the old way and the new way are worth careful consideration.<\/p><p>[&#8230;]<\/p><p>Observations is an Apple-provided way for one object to subscribe to long-running changes to some other, @Observable-macro&rsquo;ed object. It is written to use the <a href=\"https:\/\/developer.apple.com\/documentation\/Swift\/AsyncSequence\">AsyncSequence protocol<\/a>. Despite the fact that <code>withObservationTracking<\/code> was released in OS 17, Observations has not been back-ported and requires OS 26.<\/p><p>[&#8230;]<\/p><p>To implement cancellation you need to wrap the entire thing in a Task, store that task in an instance variable, and determine key points in the lifecycle of your object to cancel that task[&#8230;]<\/p><p>[&#8230;]<\/p><p>It is important that your <code>Task<\/code> and your <code>Observations<\/code> structs weakly-capture both <code>self<\/code> and whatever object you&rsquo;re trying to observe. But it&rsquo;s still easy to get it wrong.<\/p><p>[&#8230;]<\/p><p>You can&rsquo;t access a mutable <code>var<\/code> property from the deinit. You would need to wrap that property in some kind of synchronization box, like a <a href=\"https:\/\/developer.apple.com\/documentation\/synchronization\/mutex\">Mutex<\/a>, which comes with its own kinds of hassles and boilerplate.<\/p><\/blockquote>\n\n<p>Combine was much more succinct, but it seems like this API is a work in progress so it may get better next year.<\/p>\n\n<p><a href=\"https:\/\/www.hackingwithswift.com\/articles\/277\/whats-new-in-swift-6-2\">Paul Hudson<\/a> (<a href=\"https:\/\/mastodon.social\/@twostraws\/114479690308964815\">Mastodon<\/a>, <a href=\"https:\/\/news.ycombinator.com\/item?id=43940539\">Hacker News<\/a>):<\/p>\n<blockquote cite=\"https:\/\/www.hackingwithswift.com\/articles\/277\/whats-new-in-swift-6-2\">\n<p>There are a handful of important usage notes you should be aware of when using <code>Observations<\/code>:<\/p>\n<ol>\n<li>It will emit the initial value as well as all future values.<\/li>\n<li>If multiple changes come in at the same time, they might be coalesced into a single value being emitted. For example, if our <code>Task<\/code> code incremented <code>score<\/code> twice, the values emitted would go up in 2s.<\/li>\n<li>The <code>AsyncSequence<\/code> of values being emitted can potentially run forever, so you should put it on a separate task or otherwise handle it carefully.<\/li>\n<li>If you want iteration to stop &#x2013; to end the loop &#x2013; you should make the value being observed optional, then set it to nil.<\/li><\/ol>\n<\/blockquote>\n\n<p><a href=\"https:\/\/www.reddit.com\/r\/swift\/comments\/1lihdq9\/the_state_of_observability_after_wwdc25\/\">Lucas van Dongen<\/a>:<\/p>\n<blockquote cite=\"https:\/\/www.reddit.com\/r\/swift\/comments\/1lihdq9\/the_state_of_observability_after_wwdc25\/\">\n<p>I built a <a href=\"https:\/\/github.com\/LucasVanDongen\/Modern-Concurrency-2025\">simple demo app<\/a> with both the new and existing stuff.<\/p>\n<\/blockquote>\n\n<p>Previously:<\/p>\n<ul>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2025\/10\/29\/swift-6-2\/\">Swift 6.2<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2025\/10\/29\/swift-6-2-notificationcenter-messages\/\">Swift 6.2: NotificationCenter Messages<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2025\/10\/14\/cultivated-task-cancellation\/\">Cultivated Task Cancellation<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2025\/06\/18\/swiftui-at-wwdc-2025\/\">SwiftUI at WWDC 2025<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2025\/06\/18\/automatic-observation-tracking-in-uikit-and-appkit\/\">Automatic Observation Tracking in UIKit and AppKit<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2024\/08\/30\/cancellable-withobservationtracking-in-swift\/\">Cancellable withObservationTracking in Swift<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2023\/07\/06\/swiftui-data-flow-2023\/\">SwiftUI Data Flow 2023<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2022\/12\/16\/swift-pitch-observation\/\">Swift Pitch: Observation<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2022\/03\/28\/swift-async-algorithms-package\/\">Swift &ldquo;Async Algorithms&rdquo; Package<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2021\/01\/20\/swift-asyncsequence\/\">Swift AsyncSequence<\/a><\/li>\n<\/ul>","protected":false},"excerpt":{"rendered":"<p>Holly Borla: Swift 6.2 enables streaming transactional state changes of observable types using the new Observations async sequence type. Updates include all synchronous changes to the observable properties, and the transaction ends at the next await that suspends. This avoids redundant UI updates, improves performance, and ensures that your code reacts to a consistent snapshot [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"apple_news_api_created_at":"2025-10-31T17:27:37Z","apple_news_api_id":"8fba606f-6488-4ddb-a5ea-9caa65359fd3","apple_news_api_modified_at":"2025-10-31T17:27:37Z","apple_news_api_revision":"AAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/w==","apple_news_api_share_url":"https:\/\/apple.news\/Aj7pgb2SITdul6pyqZTWf0w","apple_news_coverimage":0,"apple_news_coverimage_caption":"","apple_news_is_hidden":false,"apple_news_is_paid":false,"apple_news_is_preview":false,"apple_news_is_sponsored":false,"apple_news_maturity_rating":"","apple_news_metadata":"\"\"","apple_news_pullquote":"","apple_news_pullquote_position":"","apple_news_slug":"","apple_news_sections":"\"\"","apple_news_suppress_video_url":false,"apple_news_use_image_component":false,"footnotes":""},"categories":[4],"tags":[1813,31,2741,30,2742,571,71,2200,2854,901,1812],"class_list":["post-49860","post","type-post","status-publish","format-standard","hentry","category-programming-category","tag-combine-framework","tag-ios","tag-ios-26","tag-mac","tag-macos-tahoe-26","tag-memory-management","tag-programming","tag-swift-concurrency","tag-swift-foundation","tag-swift-programming-language","tag-swiftui"],"apple_news_notices":[],"_links":{"self":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/49860","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/comments?post=49860"}],"version-history":[{"count":1,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/49860\/revisions"}],"predecessor-version":[{"id":49861,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/49860\/revisions\/49861"}],"wp:attachment":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/media?parent=49860"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/categories?post=49860"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/tags?post=49860"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}