{"id":42307,"date":"2024-03-01T14:18:51","date_gmt":"2024-03-01T19:18:51","guid":{"rendered":"https:\/\/mjtsai.com\/blog\/?p=42307"},"modified":"2024-03-01T14:19:15","modified_gmt":"2024-03-01T19:19:15","slug":"where-view-task-gets-its-main-actor-isolation-from","status":"publish","type":"post","link":"https:\/\/mjtsai.com\/blog\/2024\/03\/01\/where-view-task-gets-its-main-actor-isolation-from\/","title":{"rendered":"Where View.task Gets Its Main-actor Isolation From"},"content":{"rendered":"<p><a href=\"https:\/\/oleb.net\/2022\/swiftui-task-mainactor\/\">Ole Begemann<\/a>:<\/p>\n<blockquote cite=\"https:\/\/oleb.net\/2022\/swiftui-task-mainactor\/\">\n<p>SwiftUI&rsquo;s <a href=\"https:\/\/developer.apple.com\/documentation\/swiftui\/view\/task(priority:_:)\"><code>.task<\/code> modifier<\/a> inherits its actor context from the surrounding function. If you call <code>.task<\/code> inside a view&rsquo;s <code>body<\/code> property, the async operation will run on <a href=\"https:\/\/developer.apple.com\/documentation\/swift\/mainactor\">the main actor<\/a> because <code>View.body<\/code> is (semi-secretly) annotated with <code>@MainActor<\/code>. However, if you call <code>.task<\/code> from a helper property or function that isn&rsquo;t <code>@MainActor<\/code>-annotated, the async operation will run in the cooperative thread pool.<\/p>\n<p>[&#8230;]<\/p>\n<ol>\n  <li>\n    <p>The <a href=\"https:\/\/developer.apple.com\/documentation\/swiftui\/view\"><code>View<\/code><\/a> protocol annotates its <a href=\"https:\/\/developer.apple.com\/documentation\/swiftui\/view\/body-swift.property\"><code>body<\/code><\/a> property with <code>@MainActor<\/code>. This transfers to all conforming types.<\/p>\n  <\/li>\n  <li>\n    <p><a href=\"https:\/\/developer.apple.com\/documentation\/swiftui\/view\/task(priority:_:)\"><code>View.task<\/code><\/a> annotates its <code>action<\/code> parameter with <a href=\"https:\/\/github.com\/apple\/swift\/blob\/main\/docs\/ReferenceGuides\/UnderscoredAttributes.md#_inheritactorcontext\"><code>@_inheritActorContext<\/code><\/a>, causing it to adopt the actor context from its use site.<\/p>\n  <\/li>\n<\/ol>\n\n<p>Sadly, none of these annotations are visible in the SwiftUI documentation, making it very difficult to understand what&rsquo;s going on. [&#8230;] To really see the declarations the compiler sees, we need to look at SwiftUI&rsquo;s <em>module interface<\/em> file.<\/p>\n<p>[&#8230;]<\/p>\n<p>When used outside of <code>body<\/code>, there is no implicit <code>@MainActor<\/code> annotation, so <code>task<\/code> will run its operation on the cooperative thread pool by default. (Unless the view contains an <code>@ObservedObject<\/code> or <code>@StateObject<\/code> property, which somehow makes the entire view <code>@MainActor<\/code>. But that&rsquo;s a different topic.)<\/p>\n<\/blockquote>\n\n<p><a href=\"https:\/\/twitter.com\/teilweise\/status\/1580105376913297409\">Der Teilweise<\/a>:<\/p>\n<blockquote cite=\"https:\/\/twitter.com\/teilweise\/status\/1580105376913297409\">\n<p>Swift concurrency is a mess.<\/p>\n<p>Every software framework that tries to automagically do &ldquo;the right thing&rdquo; is doomed to break your code in the most unexpected way.<\/p>\n<p>Add a new member variable and it changes execution of unrelated parts from concurrent to serial. How wild is this?<\/p>\n<\/blockquote>\n\n<p><a href=\"https:\/\/forums.swift.org\/t\/reconsider-inference-of-global-actor-based-on-property-wrappers\/60821\">BJ Homer<\/a>:<\/p>\n<blockquote cite=\"https:\/\/forums.swift.org\/t\/reconsider-inference-of-global-actor-based-on-property-wrappers\/60821\"><p>Currently, we can infer <code>@MainActor<\/code> on an entire type based on the presence of certain property wrappers within that type. TL;DR: I&rsquo;m making a case that that&rsquo;s a bad idea, and we should reconsider it if possible.<\/p><\/blockquote>\n\n<p>Previously:<\/p>\n<ul>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2024\/03\/01\/how-the-swift-compiler-knows-that-dispatchqueue-main-implies-mainactor\/\">How the Swift Compiler Knows That DispatchQueue.main Implies @MainActor<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2022\/07\/05\/the-power-of-swiftui-task-view-modifier\/\">The Power of SwiftUI &ldquo;task&rdquo; View Modifier<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2022\/05\/12\/mainactor-not-guaranteed\/\">@MainActor Not Guaranteed<\/a><\/li>\n<\/ul>","protected":false},"excerpt":{"rendered":"<p>Ole Begemann: SwiftUI&rsquo;s .task modifier inherits its actor context from the surrounding function. If you call .task inside a view&rsquo;s body property, the async operation will run on the main actor because View.body is (semi-secretly) annotated with @MainActor. However, if you call .task from a helper property or function that isn&rsquo;t @MainActor-annotated, the async operation [&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":"2024-03-01T19:18:54Z","apple_news_api_id":"0dd23c53-7070-4625-b4ca-1b9da6d535f1","apple_news_api_modified_at":"2024-03-01T19:19:17Z","apple_news_api_revision":"AAAAAAAAAAAAAAAAAAAAAA==","apple_news_api_share_url":"https:\/\/apple.news\/ADdI8U3BwRiW0yhudptU18Q","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":[31,2078,30,2077,71,2200,901,1812],"class_list":["post-42307","post","type-post","status-publish","format-standard","hentry","category-programming-category","tag-ios","tag-ios-15","tag-mac","tag-macos-12","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\/42307","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=42307"}],"version-history":[{"count":2,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/42307\/revisions"}],"predecessor-version":[{"id":42311,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/42307\/revisions\/42311"}],"wp:attachment":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/media?parent=42307"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/categories?post=42307"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/tags?post=42307"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}