I'm currently in the process of migrating to Swift 6. A lot of my code triggers the warning from the title. Passing argument of non-sendable type 'ContentView' outside of main actor-isolated context may introduce data races. I depend on the .task/.refreshable modifiers and buttons that trigger asynchronous work that cannot be done on the Main Actor since it takes way to long.
The below code demonstrates the problem. Some comments explain my problems further. I read a lot of articles and documentations but couldn't find an answer to such a seemingly simple error
struct ContentView: View { // Marking Senable as suggested by the warning causes different warning for @State
@State private var authorizationStatus: MusicAuthorization.Status = .notDetermined // Sole purpose to trigger the errors
var body: some View {
VStack {
Text("Hello, world!")
Button("Some button") {
Task {
await doingSomeAsyncWork()
// WARNING: Passing argument of non-sendable type 'ContentView' outside of main actor-isolated context may introduce data races
}
}
}
.task { // Or refreshable I believe both behave the same
await doingSomeAsyncWork()
// WARNING: Passing argument of non-sendable type 'ContentView' outside of main actor-isolated context may introduce data races
}
}
// Marking @MainActor is not an option since some of these functions might be running for more than 10 seconds
// Tried marking func as nonisolated but that obviously had no effect
func doingSomeAsyncWork() async {
authorizationStatus = await MusicAuthorization.request() // Just to have a easy asynchronous function. Without some async code in here, the errors disappear
}
}
Thank you
Dive into the world of programming languages used for app development.
Post
Replies
Boosts
Views
Activity
hi
im fairly new to coding.. about a month, just so u know.. 😇
I am going through the tasks in (Swift Playgrounds) and I am currently in (Learning to code 2) the chapter is (Random gems everywhere) and I tried every possible solution to complete it, but to no avail!
any suggestions, comments, or corrections or tips, would be greatly appreciated!
I will attach some screenshots for a reference of what I'm trying to accomplish..
When adopting Swift 6, it’s common to encounter frameworks and libraries that haven’t been audited for sendability. I get pinged about this regularly, so I decided to write up my take on it.
If you have questions or comments, put them in a new thread. Use the Programming Languages > Swift subtopic and tag it with Concurrency; that way I’ll be sure to I see it.
IMPORTANT This is covered really well in the official documentation. Specifically, look at the Under-Specified Protocol section of Migrating to Swift 6. I wrote this up most as an excuse to get it all straight in my head.
Oh, one last thing: This is all based on the Swift 6 compiler in Xcode 16.0b4. Swift concurrency is evolving rapidly, so you might see different results in newer or older compilers.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
Implementing a Main Actor Protocol That’s Not @MainActor
Imagine you’re using the WaffleOMatic framework. It has a WaffleVarnisher class like this:
class WaffleVarnisher {
weak var delegate: Delegate?
protocol Delegate: AnyObject {
func varnisher(_ varnisher: WaffleVarnisher, didVarnish waffle: Waffle)
}
}
class Waffle {
var isGlossy: Bool = false
}
You are absolutely sure that the varnisher calls its delegate on the main thread, but the framework hasn’t been audited for sendability [1]. When you adopt it in a main-actor class, you hit this problem:
@MainActor
class WaffleState: WaffleVarnisher.Delegate {
var lastWaffle: Waffle? = nil
func varnisher(_ varnisher: WaffleVarnisher, didVarnish waffle: Waffle) {
// ^ Main actor-isolated instance method 'varnished(_:didVarnish:)'
// cannot be used to satisfy nonisolated protocol requirement
self.lastWaffle = waffle
}
}
That error has three fix-its:
Add 'nonisolated' to 'varnished(_:didVarnish:)' to make this instance method not isolated to the actor
Add '@preconcurrency' to the 'Delegate' conformance to defer isolation checking to run time
Mark the protocol requirement 'varnished(_:didVarnish:)' 'async' to allow actor-isolated conformances
I’ll discuss each in turn, albeit out of order.
[1] If it had, WaffleVarnisher.Delegate would be annotated with the @MainActor attribute.
Fix-it 3: Apply async
If you choose fix-it 3, Mark the protocol requirement 'varnished(_:didVarnish:)' 'async' to allow actor-isolated conformances, the compiler changes the varnished(_:didVarnish:) to be async:
class WaffleVarnisher {
…
protocol Delegate: AnyObject {
func varnisher(_ varnisher: WaffleVarnisher, didVarnish waffle: Waffle) async
}
}
This is a non-starter because one of our assumptions is that you can’t change the WaffleOMatic framework [1].
[1] If you could, you’d add the @MainActor attribute to WaffleVarnisher.Delegate and this whole problem goes away.
Fix-it 1: Apply non-isolated
If you choose fix-it 1, Add 'nonisolated' to 'varnished(_:didVarnish:)' to make this instance method not isolated to the actor, you get this:
@MainActor
class WaffleState1: WaffleVarnisher.Delegate {
var lastWaffle: Waffle? = nil
nonisolated func varnisher(_ varnisher: WaffleVarnisher, didVarnish waffle: Waffle) {
self.lastWaffle = waffle
// ^ Main actor-isolated property 'lastWaffle' can not be mutated from a non-isolated context
}
}
It’s fixed the original error but now you have a new one. The protocol method is non-isolated, so it can’t access the main-actor-only lastWaffle property.
You can work around this with assumeIsolated(…), but this yields another error:
@MainActor
class WaffleState1: WaffleVarnisher.Delegate {
var lastWaffle: Waffle? = nil
nonisolated func varnisher(_ varnisher: WaffleVarnisher, didVarnish waffle: Waffle) {
// A
MainActor.assumeIsolated {
// B
self.lastWaffle = waffle
// ^ Sending 'waffle' risks causing data races
}
}
}
You’re now passing the waffle object from a non-isolated context (A) to the main-actor-isolated context (B), and you can’t do that because that object is not sendable [1].
You can’t make Waffle sendable because you don’t own the WaffleOMatic framework. That leaves two options. The first is to extract sendable properties from waffle and pass them between the isolation contexts. For example, imagine that you only care about the isGlossy property of the last waffle. In that case, you might write code like this:
@MainActor
class WaffleState1: WaffleVarnisher.Delegate {
var wasLastWaffleGlossy: Bool? = nil
nonisolated func varnisher(_ varnisher: WaffleVarnisher, didVarnish waffle: Waffle) {
let wasGlossy = waffle.isGlossy
MainActor.assumeIsolated {
self.wasLastWaffleGlossy = wasGlossy
}
}
}
Problem solved!
The other option is to disable concurrency checking. There are a variety of ways you might do that. For example, you might apply @preconcurrency on the import, or use an @unchecked Sendable box to transport the waffle, or whatever. I’m not going to discuss these options in detail here because they run counter to the overall goal of Swift concurrency.
[1] Of course both of these contexts are the same!, that is, the main actor context. However, the Swift compiler doesn’t know that. Remember that the goal of Swift concurrency is to have your concurrency checked at compile time, so it’s critical to view errors like this from the perspective of the compiler.
Fix-it 2: Apply preconcurrency
If you choose fix-it 2, Add '@preconcurrency' to the 'Delegate' conformance to defer isolation checking to run time, you get this [1]:
@MainActor
class WaffleState3: @preconcurrency WaffleVarnisher.Delegate {
var lastWaffle: Waffle? = nil
func varnisher(_ varnisher: WaffleVarnisher, didVarnish waffle: Waffle) {
self.lastWaffle = waffle
}
}
This is the best solution to this problem IMO. In this context the @preconcurrency attribute [2] does two things:
It tells the compiler that it can assume that the WaffleVarnisher.Delegate methods are called in the appropriate isolation context for this type. In that case that means the main actor.
It inserts runtime checks to these delegate methods to verify that assumption.
The key advantage of fix-it 2 over fix-it 1 is that compiler knows that the delegate callback is isolated to the main actor, and so:
It doesn’t complain when you access main-actor-isolated constructs like lastWaffle.
It knows that you’re not smuggling waffles across state lines isolation contexts.
[1] Or it will, once we fix the fix-it (r. 132570262) (-:
[2] The @preconcurrency attribute has very different different meanings depending on the context!
Synchronous Results
The advantages of fix-it 2 increase when the delegate protocol includes methods that return a result synchronously. Imagine that the WaffleVarnisher.Delegate protocol has a second callback like this:
class WaffleVarnisher {
…
protocol Delegate: AnyObject {
func varnisher(_ varnisher: WaffleVarnisher, shouldMakeGlossy waffle: Waffle) -> Bool
…
}
}
The fix-it 2 approach lets you implement that delegate using state that’s isolated to the main actor:
@MainActor
class WaffleState: @preconcurrency WaffleVarnisher.Delegate {
var lastWaffle: Waffle? = nil
func varnisher(_ varnisher: WaffleVarnisher, shouldMakeGlossy waffle: Waffle) -> Bool {
return !(self.lastWaffle?.isGlossy ?? false)
}
…
}
In this case it’s possible to solve this problem with the fix-it 1 approach as well, but the code is uglier:
nonisolated func varnisher(_ varnisher: WaffleVarnisher, shouldMakeGlossy waffle: Waffle) -> Bool {
return MainActor.assumeIsolated {
return !(self.lastWaffle?.isGlossy ?? false)
}
}
However, that doesn’t always work. If the delegate method returns a non-sendable type, this approach will fail with a does not conform to the 'Sendable' protocol error.
I'm going through the migration to Swift 6 and I am running up with a few things. I have two view controllers which conform to the CLLocationManagerDelegate protocol. Both methods of the delegate have the same issue in my code. Below is an example of the warning received.
Main actor-isolated instance method 'locationManagerDidChangeAuthorization' cannot be used to satisfy nonisolated protocol requirement; this is an error in the Swift 6 language mode
Developer Community,
I've noticed a significant change in concurrent task execution behavior when testing on macOS 15 beta 4 & Xcode 16 Beta 4 compared to previous versions. Tasks that previously ran concurrently now appear to execute sequentially, impacting performance and potentially affecting apps relying on concurrent execution.
To illustrate this, I've created a simple toy example:
import SwiftUI
struct ContentView: View {
@State private var results: [String] = []
var body: some View {
VStack {
Button("Run Concurrent Tasks") {
results.removeAll()
runTasks()
}
ForEach(results, id: \.self) { result in
Text(result)
}
}
}
func runTasks() {
Task {
async let task1 = countingTask(name: "Task 1", target: 1000)
async let task2 = countingTask(name: "Task 2", target: 5000)
async let task3 = countingTask(name: "Task 3", target: 1500)
let allResults = await [task1, task2, task3]
results = allResults
}
}
func countingTask(name: String, target: Int) async -> String {
print("\(name) started")
var count = 0
for _ in 0..<target {
count += 1
}
print("\(name) finished. Count: \(count)")
return "\(name) completed. Count: \(count)"
}
}
Observed behavior (macOS 15 Beta 4 & Xcode 16 Beta 4):
Tasks appear to execute sequentially:
Task 1 started
Task 1 finished. Count: 1000
Task 2 started
Task 2 finished. Count: 5000
Task 3 started
Task 3 finished. Count: 1500
Expected behavior:
Tasks start almost simultaneously and finish based on their workload:
Task 1 started
Task 2 started
Task 3 started
Task 1 finished. Count: 1000
Task 3 finished. Count: 1500
Task 2 finished. Count: 5000
Observed behavior in macOS 15 Beta:
The profile reveals that the tasks are executing sequentially. This is evidenced by each task starting only after the previous one has completed.
Hi everyone,
when I was doing some testing on macOS 15 + Xcode 16 Beta 4 I noticed that my app's performance took a significant hit. A simple task that previously was completed within 15 seconds or less now took about a minute to complete.
I came to the conclusion that the only plausible cause could be the way .task {} and asynchronous functions are handled.
Starting several .task{} and calling async functions from within using macOS 14.5 and Xcode 15.4 results in following log output:
task1 started
task3 started
task2 started
task4 started
--> task2 ended
--> task3 ended
--> task4 ended
--> task1 ended`
Running the same code on macOS 15.0 + Xcode 16 Beta 4 will result in the following log output:
task1 started
--> task1 ended
task2 started
--> task2 ended
task3 started
--> task3 ended
task4 started
--> task4 ended
In the first example the code is executed in 'parallel'. All tasks are started and doing there respective work. In second example a task is started and we are waiting for it to complete before the other tasks are started.
I could start to rewrite my code to get the results I desire, however I'm wondering if this is a bug in regards to macOS 15 + Xcode 16 Beta 4 and the way .task {} and asynchronous functions are handled. The output is quite different after all.
What's your take on this? If you want to try it out for yourself you can use the following sample code:
import SwiftUI
struct ContentView: View {
func func1() async -> Int {
print("task1 started")
var myInt: Int = 0
while myInt < 999999999 {
myInt += 1
}
print(" --> task1 ended")
return 1
}
func func2() async -> Int {
print("task2 started")
var myInt: Int = 0
while myInt < 999999 {
myInt += 1
}
print(" --> task2 ended")
return 2
}
func func3() async -> Int {
print("task3 started")
var myInt: Int = 0
while myInt < 999999 {
myInt += 1
}
print(" --> task3 ended")
return 3
}
func func4() async -> Int {
print("task4 started")
var myInt: Int = 0
while myInt < 999999999 {
myInt += 1
}
print(" --> task4 ended")
return 4
}
var body: some View {
VStack {
Text("Hello, world!")
}
.task {
await func1()
}
.task {
await func2()
}
.task {
await func3()
}
.task {
await func4()
}
}
}
#Preview {
ContentView()
}
So any time I create a class that's both @Observable and Codable, e.g.
@Observable class GameLocationManager : Codable {
I get a warning in the macro expansion code:
@ObservationIgnored private let _$observationRegistrar = Observation.ObservationRegistrar()
Immutable property will not be decoded because it is declared with an initial value which cannot be overwritten.
I've been ignoring them for now, but there are at least a half a dozen of them now in my (relatively small) codebase, and I'd like to find a solution (ideally one that doesn't require me to write init(decoder:) for every @Observable class in my project...), especially since I'm not sure what the actual consequences of ignoring this might be.
Hi, I'm new to swift but have experience with coding in general. Following the app dev training tutorial, came across this line of code:
var wrapper: ErrorWrapper {
ErrorWrapper(error: someVal)
}
My question is, why not just do this...
var wrapper: ErrorWrapper = ErrorWrapper(error: someVal)
Is it a conventions thing or is there some purpose, code seems to work either way. My understanding of closures is that they are just lambda functions, so in the first codeblock, all it's doing is calling a function that returns the instantiated ErrorWrapper object. Why not just assign the variable to it?
Example1:
let num = NSDecimalNumber(string: "0.123456789012345678909")
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.usesGroupingSeparator = true
formatter.maximumFractionDigits = 25
formatter.minimumFractionDigits = 25
formatter.minimumIntegerDigits = 1
let str = formatter.string(from: num) ?? ""
print(str)
output
"0.1234567890123460000000000"
Example2:
let num = NSDecimalNumber(string: "12323.123456789012345678909")
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.usesGroupingSeparator = true
formatter.maximumFractionDigits = 25
formatter.minimumFractionDigits = 25
formatter.minimumIntegerDigits = 1
let str = formatter.string(from: num) ?? ""
print(str)
output
"12,323.1234567890000000000000000"
How to correctly format the contents of the above two inputs?
Hello! I have an application written in Objective-c/C++ and I would like it to have support for OTA updates.
I implemented the logic through two executable files, one of which was responsible for launching and updating the first, but this option did not pass Apple's review.
Could you tell me how this functionality can be implemented and so that it meets the requirements of the App Store.
I think I found a bug in the Objective-C++ compiler, linker, or runtime. Here’s the scenario:
We have a macOS app written in Swift.
To control hardware from a 3rd party manufacturer, we use a couple SDKs provided by the manufacturer.
The SDKs use dynamically loaded libraries and the interface is defined in C++ headers.
To bridge between our Swift code and the C++ APIs we have a private Cocoapod that wraps the 3rd party interface with Objective-C++ classes.
The two SDKs each provide an interface for discovering attached devices using a callback class that the programmer provides. By accident we named both callback implementations DiscoveryCallback, but this was not a compiler error because neither class was publicly declared, and each was defined in the .mm file where it was used.
However, the problem we’re seeing is this:
We want to discover Videohub devices, so we register a new instance of DiscoveryCallback (defined in the same .mm file as this code) with the Videohub SDK.
A Videohub device is connected and the SDK calls a method on our callback.
Surprise! The callback we registered in step 1 was actually the one intended for Decklink devices, defined in a completely different .mm file.
This violates all sorts of assumptions and our app quickly crashes.
The funny thing is, the two implementations of DiscoveryCallback have completely different method names. The Videohub SDK is supposed to be calling NewVideohubDevice, yet somehow it successfully calls DeckLinkDeviceArrived on an instance of a class it shouldn’t even know about.
So the compiler has checked that our intended DiscoveryCallback matches the protocol that the SDK expects, but at runtime the compiled code instantiates a completely different implementation of DiscoveryCallback and somehow doesn’t immediately fail; we still call a method on it that doesn’t even share a name with the intended target. I imagine at this point the method names are long forgotten and are just pointers in a table.
I don’t know if this is a bug in the compiler, the Objective-C++ runtime, or if this is just “working as designed” undefined behavior that I should have avoided by not giving two private classes the same name. I know it’s possible to use a private API simply by redeclaring it in my own code, and this seems related to that, but I feel like the compiler or linker should have warned me that I had two implementations of the same class, or if that is not an error, then the runtime should have instantiated the class that was privately defined in the same source file where it was used.
Obviously I can’t share our entire project; I’d like to provide some sample code that replicates the issue, but I don’t have time to do that right now. I’m posting this to see if other developers have had a similar experience.
When i try to run break statement in default case, it doesn't run and shows 'Closure containing control flow statement cannot be used with result builder 'ViewBuilder'' error.Why it doesn't run and how to solve this trouble?
Hi, everyone. I am trying to print out “Hello, world” by creating a Command Line tool project. I try to type “swiftc Hello.swift” to compile it in the terminal but instead of having a file named Hello.swift, I have a file named by default Hello.xcodeprojec. How I can solve this issue?
For some time now Xcode has been downloading crash reports from users of my app about crashes related to arrays. One of them looks like this:
...
Code Type: ARM-64
Parent Process: launchd [1]
User ID: 501
Date/Time: 2024-07-18 14:59:40.4375 +0800
OS Version: macOS 15.0 (24A5289h)
...
Crashed Thread: 0
Exception Type: EXC_BREAKPOINT (SIGTRAP)
Exception Codes: 0x0000000000000001, 0x00000001045048b8
Termination Reason: Namespace SIGNAL, Code 5 Trace/BPT trap: 5
Terminating Process: exc handler [1771]
Thread 0 Crashed:
0 MyApp 0x00000001045048b8 specialized Collection.map<A>(_:) + 596
1 MyApp 0x00000001045011e4 MyViewController.validateToolbarButtons() + 648 (MyViewController.swift:742)
...
The relevant code looks like this:
class MyViewController {
func validateToolbarButtons() {
let indexes = tableView.clickedRow == -1 || tableView.selectedRowIndexes.contains(tableView.clickedRow) ? tableView.selectedRowIndexes : IndexSet(integer: tableView.clickedRow)
let items = indexes.map({ myArray[$0] })
...
}
}
The second crash looks like this:
...
Code Type: X86-64 (Native)
Parent Process: launchd [1]
User ID: 502
Date/Time: 2024-07-15 15:53:35.2229 -0400
OS Version: macOS 15.0 (24A5289h)
...
Crashed Thread: 0
Exception Type: EXC_BAD_INSTRUCTION (SIGILL)
Exception Codes: 0x0000000000000001, 0x0000000000000000
Termination Reason: Namespace SIGNAL, Code 4 Illegal instruction: 4
Terminating Process: exc handler [13244]
Thread 0 Crashed:
0 libswiftCore.dylib 0x00007ff812904fc0 _assertionFailure(_:_:flags:) + 288
1 MyApp 0x0000000101a31e04 specialized _ArrayBuffer._getElementSlowPath(_:) + 516
2 MyApp 0x00000001019d04eb MyObject.myProperty.setter + 203 (MyObject.swift:706)
3 MyApp 0x000000010192f66e MyViewController.controlTextDidChange(_:) + 190 (MyViewController.swift:166)
...
And the relevant code looks like this:
class MyObject {
var myProperty: [MyObject] {
get {
...
}
set {
let items = newValue.map({ $0.id })
...
}
}
}
What could cause such crashes? Could they be caused by anything other than concurrent access from multiple threads (which I'm quite sure is not the case here, as I only access these arrays from the main thread)?
I am getting an following error while compiling the
Fortran file with "gfortran TEST_1_fortran_only_fixed.f"
ld: unsupported tapi file type '!tapi-tbd' in YAML file '/Library/Developer/CommandLineTools/SDKs/MacOSX14.sdk/usr/lib/libSystem.tbd' for architecture x86_64
collect2: error: ld returned 1 exit status
Please help me to solve this issue
Just when I think I am finally starting to understand Swift I come across a gotcha like the following:
I have two object, a swiftui display and one of data to be displayed. Ok, sounds easy.
The data is read out of a JSON file so I have a set of arrays and dictionaries. The data is valid when read, it is definitely there, but when I go to display it, its gone. Just vanished. Wasted about a day on this so far, and I’ve seen it before, the inability to pass out of an object an array or dictionary with contents intact.
If I create an array var, and not let the system do it, contents are preserved. So, in the data object I’ll have something like this:
struct DataObject{
var item: [String:Any]
item=JSONData serialized out of memory, and may have say, 12 fields
}
In my SwiftUI module I have:
var item=dataObject.item
dataObject.item now has 0 fields.
I can allocate and initialize a dictionary in DataObject and those elements come through fine. So it seems like the stuff being serialized from JSON is being deleted out from under me.
Hi all,
I just wanted to ask how people were using ModelActor with the Swift 6 language mode enabled. My current implementation involves passing the ModelContainer to my ModelActor, which worked in Sonoma and previous betas of Sequoia, however in the current Beta 3, I get this error:
"Sending 'self.modelContext.container' risks causing data races"
I am a bit confused by this, as from what I understand, ModelContainer conforms to Sendable, so ideally this error should not be thrown. Is this a bug in Beta 3?
Thanks in advance.
Why doesn’t deinit support async? At the end of a test, I want to wipe data from HealthKit, and it’s delete function is asynchronous.
We use XCode 15.2 to build our application, but we found that the app crashes on iOS 12.1.1, while it works without crashing on iOS 12.5.7. We discovered that the crash is related to ImageLoaderMachO::doModInitFunctions. We're not sure what is causing this.
When I run the code, xCode shows me this error "Expressions are not allowed at the top level" While the code is correct and runs in the main.swift file only. And also runs in online compilers. please give me solution...