{"id":37920,"date":"2022-12-16T14:30:09","date_gmt":"2022-12-16T19:30:09","guid":{"rendered":"https:\/\/mjtsai.com\/blog\/?p=37920"},"modified":"2023-04-21T20:51:51","modified_gmt":"2023-04-22T00:51:51","slug":"swift-pitch-observation","status":"publish","type":"post","link":"https:\/\/mjtsai.com\/blog\/2022\/12\/16\/swift-pitch-observation\/","title":{"rendered":"Swift Pitch: Observation"},"content":{"rendered":"<p><a href=\"https:\/\/forums.swift.org\/t\/pitch-observation\/62051\">Philippe Hausler<\/a>:<\/p>\n<blockquote cite=\"https:\/\/forums.swift.org\/t\/pitch-observation\/62051\"><p>There are already a few mechanisms for observation in Swift. Some of these include Key Value Observing (KVO), or <code>ObservableObject<\/code>; but those are relegated to in the case of KVO to just <code>NSObject<\/code> descendants, and <code>ObservableObject<\/code> requires using Combine which is restricted to Darwin platforms and does not leverage language features like async\/await or <code>AsyncSequence<\/code>. By taking experience from those existing systems we can build a more generally useful feature that applies to all Swift reference types; not just those that inherit from <code>NSObject<\/code> and have it work cross platform with using the advantages from low level language features like async\/await.<\/p><p>[&#8230;]<\/p><p>Combine&rsquo;s <code>ObservableObject<\/code> produces changes on the leading edge of the will\/did events and all delivered values are before the value did set. Albeit this serves SwiftUI well, it is restrictive for non SwiftUI usage and can be surprising to developers first encountering that restriction.<\/p><p>[&#8230;]<\/p><p>The <code>Observable<\/code> protocol includes a set of extension methods to handle observation. In the simplest, most common case, a client can use the <code>changes(for:)<\/code> method to observe changes to that field for a given instance. [&#8230;] This allows users of this protocol to be able to observe the changes to specific values either as a distinct step in the chain of change events or as an asynchronous sequence of change events.<\/p><p>[&#8230;]<\/p><p>By default the concept of observation is transitively forwarded; observing a keypath of <code>\\A.b.c.d<\/code> means that if the field <code>.b<\/code> is <code>Observable<\/code> that is registered with an observer to track <code>\\B.c.d<\/code> and so on. This means that graphs of observations can be tracked such that any set of changes are forwarded as an event.<\/p><p>[&#8230;]<\/p><p>The <code>ObservationTracking<\/code> mechanism is the primary interface designed for the purposes to interoperate with SwiftUI. Views will register via the <code>withTracking<\/code> method such that if in the execution of <code>body<\/code> any field is accessed in an <code>Observable<\/code> that field is registered into the access set that will be indicated in the handler passed to the <code>addChangeHandler<\/code> function. If at any point in time that handler needs to be directly invalidated the <code>invalidate<\/code> function can be invoked; which will remove all change handlers registered to the <code>Observable<\/code> instances under the tracking.<\/p>\n<p>[&#8230;]<\/p>\n<p>A default implementation can be accomplished in generality by a <a href=\"https:\/\/forums.swift.org\/t\/pitch-type-wrappers\/60019\">type wrapper<\/a> that intercepts the modifications of any field on the <code>Observable <\/code>. The type wrapper <code>DefaultObservable<\/code> provides default implementations for the type where the associated <code>Observation<\/code> type is <code>ObservationTracking.Token<\/code>. This means that developers have the flexibility of an easy to use interface that progressively allows for more and more detailed control: starting from mere annotation that grants general observability, progressing to delegation to a storage mechanism that manages registering and unregistering observers, to full control of observation.<\/p><\/blockquote>\n\n<p><a href=\"https:\/\/forums.swift.org\/t\/pitch-observation\/62051\/42\">David Smith<\/a>:<\/p>\n<blockquote cite=\"https:\/\/forums.swift.org\/t\/pitch-observation\/62051\/42\"><p>There&rsquo;s a number of issues we ran into with KVO, all around concurrency[&#8230;]<\/p><\/blockquote>\n\n<p><a href=\"https:\/\/cohost.org\/Catfish-Man\/post\/614201-continuing-the-delug\">David Smith<\/a>:<\/p>\n<blockquote cite=\"https:\/\/cohost.org\/Catfish-Man\/post\/614201-continuing-the-delug\"><p>Philippe&rsquo;s new <code>ObservationTracking<\/code> machinery reads like a shippable spiritual successor to that hack.<\/p><\/blockquote>\n\n<p id=\"swift-pitch-observation-update-2023-03-20\">Update (2023-03-20): <a href=\"https:\/\/forums.swift.org\/t\/pitch-observation-revised\/63757\">Philippe Hausler<\/a>:<\/p>\n<blockquote cite=\"https:\/\/forums.swift.org\/t\/pitch-observation-revised\/63757\"><ul><li>Pitch 1: <a href=\"https:\/\/forums.swift.org\/t\/pitch-observation\/62051\">Initial pitch<\/a><\/li><li>Pitch 2: Previously Observation registered observers directly to <code>Observable<\/code>, the new approach registers observers to an <code>Observable<\/code> via a <code>ObservationTransactionModel<\/code>. These models control the &ldquo;edge&rdquo; of where the change is emitted. They are the responsible component for notifying the observers of events. This allows the observers to focus on just the event and not worry about &ldquo;leading&rdquo; or &ldquo;trailing&rdquo; (will\/did) &ldquo;edges&rdquo; of the signal. Additionally the pitch was shifted from the type wrapper feature over to the more appropriate macro features.<\/li><li>Pitch 3: The <code>Observer<\/code> protocol and <code>addObserver(_:)<\/code> method are gone in favor of providing async sequences of changes and transactions.<\/li><\/ul><\/blockquote>\n\n<p id=\"swift-pitch-observation-update-2023-04-21\">Update (2023-04-21): <a href=\"https:\/\/forums.swift.org\/t\/se-0395-observability\/64342\">Ben Cohen<\/a>:<\/p>\n<blockquote cite=\"https:\/\/forums.swift.org\/t\/se-0395-observability\/64342\"><p>The review of <a href=\"https:\/\/github.com\/apple\/swift-evolution\/blob\/main\/proposals\/0395-observability.md\">SE-0395: Observability<\/a> begins now and runs through April 24, 2023.<\/p><\/blockquote>","protected":false},"excerpt":{"rendered":"<p>Philippe Hausler: There are already a few mechanisms for observation in Swift. Some of these include Key Value Observing (KVO), or ObservableObject; but those are relegated to in the case of KVO to just NSObject descendants, and ObservableObject requires using Combine which is restricted to Darwin platforms and does not leverage language features like async\/await [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"apple_news_api_created_at":"2022-12-16T19:30:11Z","apple_news_api_id":"81c10ac6-b2f0-4074-9a61-cc7f5e4ff4db","apple_news_api_modified_at":"2023-04-22T00:51:54Z","apple_news_api_revision":"AAAAAAAAAAAAAAAAAAAAAQ==","apple_news_api_share_url":"https:\/\/apple.news\/AgcEKxrLwQHSaYcx_Xk_02w","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,800,275,46,71,2200,901,1812],"class_list":["post-37920","post","type-post","status-publish","format-standard","hentry","category-programming-category","tag-combine-framework","tag-concurrency","tag-keyvalueobserving","tag-languagedesign","tag-programming","tag-swift-concurrency","tag-swift-programming-language","tag-swiftui"],"apple_news_notices":[],"_links":{"self":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/37920","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=37920"}],"version-history":[{"count":3,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/37920\/revisions"}],"predecessor-version":[{"id":39114,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/37920\/revisions\/39114"}],"wp:attachment":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/media?parent=37920"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/categories?post=37920"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/tags?post=37920"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}