{"id":45034,"date":"2024-09-20T15:38:46","date_gmt":"2024-09-20T19:38:46","guid":{"rendered":"https:\/\/mjtsai.com\/blog\/?p=45034"},"modified":"2024-11-14T14:49:19","modified_gmt":"2024-11-14T19:49:19","slug":"unwanted-swift-concurrency-checking","status":"publish","type":"post","link":"https:\/\/mjtsai.com\/blog\/2024\/09\/20\/unwanted-swift-concurrency-checking\/","title":{"rendered":"Unwanted Swift Concurrency Checking"},"content":{"rendered":"<p>I&rsquo;m not adopting Swift Concurrency yet&mdash;it&rsquo;s not even <a href=\"https:\/\/mjtsai.com\/blog\/2021\/10\/28\/swift-concurrency-for-older-os-versions\/\">available<\/a> on the OS versions I&rsquo;m targeting&mdash;so my plan was to take advantage of the <a href=\"https:\/\/www.swift.org\/migration\/documentation\/migrationguide\/\">Swift 5 language mode<\/a> of the Swift 6 compiler:<\/p>\n<blockquote cite=\"https:\/\/www.swift.org\/migration\/documentation\/migrationguide\/\">\n<p>The Swift 6 language mode is <em>opt-in<\/em>. Existing projects will not switch to this mode without configuration changes.<\/p>\n<\/blockquote>\n<p>I had <code>SWIFT_VERSION<\/code> set to <code>5<\/code>, and this worked great when compiling my apps with beta versions of macOS 15 and Xcode 16 over the summer. I needed to make some <a href=\"https:\/\/mjtsai.com\/blog\/2024\/07\/01\/spamsieve-3-0-5\/\">minor updates<\/a> to my code to compile with Swift 6 but none were related to concurrency.<\/p>\n<p>However, after updating to the release version of Xcode 16 (since macOS 15 won&rsquo;t run Xcode 15.4), I started getting this error:<\/p>\n<blockquote>Capture of 'buffer' with non-sendable type 'UnsafeMutableBufferPointer&lt;T&gt;' in a `@Sendable` closure; this is an error in the Swift 6 language mode\n\nGeneric struct 'UnsafeMutableBufferPointer' does not conform to the 'Sendable' protocol (Swift.UnsafeMutableBufferPointer)\n<\/blockquote>\n<p>from this code:<\/p>\n<pre>DispatchQueue.concurrentPerform(iterations: count) { (index) in\n    buffer[index] = \/\/ &#8230;\n}<\/pre>\n<p>The GCD API is marked with <code>@preconcurrency<\/code>:<\/p>\n<pre>@preconcurrency public class func concurrentPerform(iterations: Int, execute work: @Sendable (Int) -&gt; Void)<\/pre>\n<p>but that didn&rsquo;t stop it from complaining.<\/p>\n<p>I don&rsquo;t know whether this is a bug, but it&rsquo;s certainly not what I expected after <a href=\"https:\/\/x.com\/hollyborla\/status\/1758298036667916370\">repeatedly hearing<\/a> that strict concurrency checking is opt-in. Reading the <a href=\"https:\/\/www.swift.org\/migration\/documentation\/swift-6-concurrency-migration-guide\/completechecking\">details<\/a>:<\/p>\n<blockquote cite=\"https:\/\/www.swift.org\/migration\/documentation\/swift-6-concurrency-migration-guide\/completechecking\">\n<p>You can address data-race safety issues in your projects module-by-module, and you can enable the compiler&rsquo;s actor isolation and <code>Sendable<\/code> checking as warnings in the Swift 5 language mode, allowing you to assess your progress toward eliminating data races before turning on the Swift 6 language mode.<\/p>\n<\/blockquote>\n<p>It seems that you can &ldquo;enable&rdquo; the checking as warnings by setting <code>SWIFT_TREAT_WARNINGS_AS_ERRORS<\/code> to <code>NO<\/code>, but I don&rsquo;t want to do that because I do want other types of warnings to be treated as errors. There&rsquo;s <a href=\"https:\/\/github.com\/swiftlang\/swift-evolution\/blob\/main\/proposals\/0443-warning-control-flags.md\">not yet<\/a> a way to control warnings individually. And, also, I don&rsquo;t want to see concurrency warnings with every build when I&rsquo;m not going to be actively fixing them for a while. There is a separate <code>SWIFT_STRICT_CONCURRENCY<\/code> build setting, but there doesn&rsquo;t seem to be a way to turn it off, only to <code>minimal<\/code>, which still reports this warning\/error.<\/p>\n<p>I&rsquo;m not the only one to run into this exact issue. <a href=\"https:\/\/forums.swift.org\/t\/dispatchqueue-concurrentperform-unsaferawpointer-in-swift-6\/74125\/2\">Frank Rupprecht<\/a> found that the warning can be avoided by assigning the closure to a variable, rather than passing it directly:<\/p>\n<pre>let closure = { (index: Int) in\n    buffer[index] = \/\/ &#8230;\n}\nDispatchQueue.concurrentPerform(iterations: count, execute: closure)<\/pre>\n<p>It&rsquo;s not clear to me whether this is a <a href=\"https:\/\/mjtsai.com\/blog\/2018\/02\/20\/when-swift-makes-you-use-throws-instead-of-rethrows\/\">hack exploiting a compiler bug<\/a> or a designated opt-out akin to how GCC treats putting an <a href=\"https:\/\/www.approxion.com\/a-gcc-compiler-mistake\/\">assignment expression in parentheses<\/a>. But I&rsquo;m going with this for now because it seems silly to <a href=\"https:\/\/forums.swift.org\/t\/dispatchqueue-concurrentperform-unsaferawpointer-in-swift-6\/74125\/3\">rewrite the code<\/a> for Swift Concurrency when (a) I&rsquo;m not using that yet, and (b) GCD is a separate world, anyway.<\/p>\n\n<p><a href=\"https:\/\/www.jessesquires.com\/blog\/2024\/06\/05\/swift-concurrency-non-sendable-closures\/\">Jesse Squires<\/a> discusses a related issue when he <em>does<\/em> have strict concurrency checking enabled:<\/p>\n<blockquote cite=\"https:\/\/www.jessesquires.com\/blog\/2024\/06\/05\/swift-concurrency-non-sendable-closures\/\"><p>I was confronted with this warning in a project recently and I want to share the hack for how I worked around it. The issue was the result of a combination of factors I mentioned above. I was interacting with <code>@preconcurrency<\/code> APIs <strong>and<\/strong> I knew my code was concurrency-safe, but I was unable to accurately express that to the compiler.<\/p><p>[&#8230;]<\/p><p>So, we have situation where the Swift compiler is telling us that the closure being captured needs to be <code>@Sendable<\/code> but we cannot make it <code>@Sendable<\/code>. It is also telling us that the closure loses its <code>@MainActor<\/code> but we know that the closure will always be called from the main queue. Because of these two problems, we need to find a way to work around the warnings and coerce the compiler into doing what we want.<\/p><\/blockquote>\n\n<p>I also got a similar error about capturing a non-sendable type when using these methods:<\/p>\n<pre>@MainActor func filePromiseProvider(_ filePromiseProvider: NSFilePromiseProvider, fileNameForType fileType: String) -&gt; String\n\nnonisolated func filePromiseProvider(_ filePromiseProvider: NSFilePromiseProvider, writePromiseTo url: URL) async throws<\/pre>\n<p>I was already using the queue supplied by the provider, but Swift Concurrency doesn&rsquo;t know that. Here I was able to silence the error by using:<\/p>\n<pre>nonisolated(unsafe) let provider = \/\/ &#8230;<\/pre>\n\n<p>Finally, I got this error:<\/p>\n\n<blockquote>Struct 'Notification' does not conform to the 'Sendable' protocol (Foundation.Notification)<\/blockquote>\n\n<p>with this code:<\/p>\n<pre>queue.async(flags: .barrier) {\n    DispatchQueue.main.sync {\n        f(notification)\n    }\n}<\/pre>\n<p>I&rsquo;m ensuring that the notification is only used on the main thread, but Swift Concurrency doesn&rsquo;t realize that. Again, I was able to silence the error using <code>nonisolated(unsafe)<\/code>. It&rsquo;s not clear to me whether <code>assumeIsolated()<\/code> might be more appropriate in this situation, but I can&rsquo;t use it, anyway, because that API doesn&rsquo;t back deploy far enough.<\/p>\n\n<p>Previously:<\/p>\n<ul>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2024\/09\/19\/swift-6\/\">Swift 6<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2024\/09\/12\/xcode-16\/\">Xcode 16<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2024\/07\/22\/swift-6-announced\/\">Swift 6 Announced<\/a><\/li>\n<\/ul>\n\n<p id=\"unwanted-swift-concurrency-checking-update-2024-09-23\">Update (2024-09-23): <a href=\"https:\/\/techhub.social\/@sjs\/113176195347387558\">Sami Samhuri<\/a>:<\/p>\n<blockquote cite=\"https:\/\/techhub.social\/@sjs\/113176195347387558\">\n<p>I ran into <a href=\"https:\/\/techhub.social\/@sjs\/113159246689487839\">something similar<\/a> and it&rsquo;s fixed in Xcode 16.1 beta 2.<\/p>\n<\/blockquote>\n<p>My issues are not fixed in the beta, so I filed <a href=\"https:\/\/github.com\/swiftlang\/swift\/issues\/76652\">a bug<\/a>.<\/p>\n\n<p><a href=\"https:\/\/mastodon.social\/@objc\/113176190218751264\">Tanner Bennett<\/a>:<\/p>\n<blockquote cite=\"https:\/\/mastodon.social\/@objc\/113176190218751264\">\n<p>At Tinder we have about 2 dozen concurrency related warnings under Xcode 16 and we have concurrency checking turned off and are in swift 5 mode. Super frustrating.<\/p>\n<\/blockquote>\n\n<p><a href=\"https:\/\/x.com\/rhysmorgan\/status\/1818950951794872327\">Rhys Morgan<\/a>:<\/p>\n<blockquote cite=\"https:\/\/x.com\/rhysmorgan\/status\/1818950951794872327\">\n<p>You know about the <code>DispatchQueue.main.async<\/code> special casing [in the compiler] too, right? When you write <em>exactly<\/em> that, it infers that the contents of the <code>async<\/code> bit are <code>MainActor<\/code>-isolated.<\/p>\n<\/blockquote>\n\n<p id=\"unwanted-swift-concurrency-checking-update-2024-11-14\">Update (2024-11-14): <a href=\"https:\/\/github.com\/swiftlang\/swift\/issues\/76652#issuecomment-2477240075\">My bug<\/a> is now marked as fixed, and hopefully this will make it into Xcode 16.2.<\/p>","protected":false},"excerpt":{"rendered":"<p>I&rsquo;m not adopting Swift Concurrency yet&mdash;it&rsquo;s not even available on the OS versions I&rsquo;m targeting&mdash;so my plan was to take advantage of the Swift 5 language mode of the Swift 6 compiler: The Swift 6 language mode is opt-in. Existing projects will not switch to this mode without configuration changes. I had SWIFT_VERSION set to [&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-09-20T19:38:48Z","apple_news_api_id":"72754051-f723-4b40-a856-ec64c70f1e1a","apple_news_api_modified_at":"2024-11-14T19:49:27Z","apple_news_api_revision":"AAAAAAAAAAAAAAAAAAAAAw==","apple_news_api_share_url":"https:\/\/apple.news\/AcnVAUfcjS0CoVuxkxw8eGg","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":[69,880,30,2598,71,2200,901],"class_list":["post-45034","post","type-post","status-publish","format-standard","hentry","category-programming-category","tag-cocoa","tag-grand-central-dispatch-gcd","tag-mac","tag-macos-15-sequoia","tag-programming","tag-swift-concurrency","tag-swift-programming-language"],"apple_news_notices":[],"_links":{"self":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/45034","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=45034"}],"version-history":[{"count":6,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/45034\/revisions"}],"predecessor-version":[{"id":45775,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/45034\/revisions\/45775"}],"wp:attachment":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/media?parent=45034"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/categories?post=45034"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/tags?post=45034"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}