Swift Concurrency Resources:
DevForums tags: Concurrency
The Swift Programming Language > Concurrency documentation
Migrating to Swift 6 documentation
WWDC 2022 Session 110351 Eliminate data races using Swift Concurrency — This ‘sailing on the sea of concurrency’ talk is a great introduction to the fundamentals.
WWDC 2021 Session 10134 Explore structured concurrency in Swift — The table that starts rolling out at around 25:45 is really helpful.
Swift Async Algorithms package
Swift Concurrency Proposal Index DevForum post
Why is flow control important? DevForums post
Matt Massicotte’s blog
Dispatch Resources:
DevForums tags: Dispatch
Dispatch documentation — Note that the Swift API and C API, while generally aligned, are different in many details. Make sure you select the right language at the top of the page.
Dispatch man pages — While the standard Dispatch documentation is good, you can still find some great tidbits in the man pages. See Reading UNIX Manual Pages. Start by reading dispatch in section 3.
WWDC 2015 Session 718 Building Responsive and Efficient Apps with GCD [1]
WWDC 2017 Session 706 Modernizing Grand Central Dispatch Usage [1]
Avoid Dispatch Global Concurrent Queues DevForums post
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
[1] These videos may or may not be available from Apple. If not, the URL should help you locate other sources of this info.
Concurrency
RSS for tagConcurrency is the notion of multiple things happening at the same time.
Posts under Concurrency tag
180 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
Hello ladies and gentlemen, I'm writing a simple renderer on the main actor using Metal and Swift 6. I am at the stage now where I want to create a render pipeline state using asynchronous API:
@MainActor
class Renderer {
let opaqueMeshRPS: MTLRenderPipelineState
init(/*...*/) async throws {
let descriptor = MTLRenderPipelineDescriptor()
// ...
opaqueMeshRPS = try await device.makeRenderPipelineState(descriptor: descriptor)
}
}
I get a compilation error if try to use the asynchronous version of the makeRenderPipelineState method:
Non-sendable type 'any MTLRenderPipelineState' returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary
Which is understandable, since MTLRenderPipelineState is not Sendable. But it looks like no matter where or how I try to access this method, I just can't do it - you have this API, but you can't use it, you can only use the synchronous versions.
Am I missing something or is Metal just not usable with Swift 6 right now?
In the header for UIViewController, the method dismissViewControllerAnimated is declared like this:
- (void)dismissViewControllerAnimated: (BOOL)flag completion: (void (^ __nullable)(void))completion NS_SWIFT_DISABLE_ASYNC API_AVAILABLE(ios(5.0));
NS_SWIFT_DISABLE_ASYNC means that there's no async version exposed like there would normally be of a method that exposes a completion handler. Why is this? And is it unwise / unsafe for me to make my own async version of it using a continuation?
My use case is that I want a method that will sequentially dismiss all view controllers presented by a root view controller. So I could have this extension on UIViewController:
extension UIViewController {
func dismissAsync(animated: Bool) async {
await withCheckedContinuation { continuation in
self.dismiss(animated: animated) {
continuation.resume()
}
}
}
func dismissPresentedViewControllers() async {
while self.topPresentedViewController != self {
await self.topPresentedViewController.dismissAsync(animated: true)
}
}
var topPresentedViewController: UIViewController {
var result = self
while result.presentedViewController != nil {
result = result.presentedViewController!
}
return result
}
I found a similar problem here https://developer.apple.com/forums/thread/764777 and I could solve my problem by wrapping the call to requestAutomaticPassPresentationSuppression in a call to DispatchQueue.global().async.
But my question is if this is really how things should work. Even with strict concurrency warnings in Swift 6 I don't get any warnings. Just a runtime crash.
How are we supposed to find these problems? Couldn't the compiler assist with a warning/error.
Why does the compiler make the assumptions it does about the method that is declared like this:
@available(iOS 9.0, *)
open class func requestAutomaticPassPresentationSuppression(responseHandler: @escaping (PKAutomaticPassPresentationSuppressionResult) -> Void) -> PKSuppressionRequestToken
Now that we have migrated to Swift 6 our code base contains a bunch of unknown places where it will crash as above.
I'm getting the following error in my SwiftUI code:
"Main actor-isolated property 'avatarImage' can not be referenced from a Sendable closure"
I don't understand how to fix it.
This happens in the following code:
You can copy-paste this into an empty project and make sure to have Swift 6 enabled under the Build Settings > Swift Language Version
import PhotosUI
import SwiftUI
public struct ContentView: View {
@State private var avatarItem: PhotosPickerItem?
@State private var avatarImage: Image?
@State private var avatarData: Data?
public var body: some View {
VStack(spacing: 30) {
VStack(alignment: .center) {
PhotosPicker(selection: $avatarItem, matching: .images) {
if let avatarImage {
avatarImage
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 100, height: 100)
.foregroundColor(.gray)
.background(.white)
.clipShape(Circle())
.opacity(0.75)
.overlay {
Image(systemName: "pencil")
.font(.title)
.shadow(radius: 5)
}
} else {
Image(systemName: "person.circle.fill")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100)
.foregroundColor(.gray)
.background(.white)
.clipShape(Circle())
.opacity(0.75)
.overlay {
Image(systemName: "pencil")
.font(.title)
.shadow(radius: 5)
}
}
}
}
}
.onChange(of: avatarItem) {
Task {
if let data = try? await avatarItem?.loadTransferable(
type: Data.self
) {
if let processed = processImage(data: data) {
avatarImage = processed.image
avatarData = processed.data
} else {
}
}
}
}
}
private func processImage(data: Data) -> (image: Image?, data: Data?)? {
guard let uiImage = UIImage(data: data)?.preparingForDisplay() else {
return nil
}
// Check original size
let sizeInMB = Double(data.count) / (1024 * 1024)
// If image is larger than 1MB, compress it
if sizeInMB > 1.0 {
guard let compressedData = uiImage.compress() else { return nil }
return (Image(uiImage: uiImage), compressedData)
}
return (Image(uiImage: uiImage), data)
}
}
#Preview {
ContentView()
}
public extension UIImage {
func compress(to maxSizeInMB: Double = 1.0) -> Data? {
let maxSizeInBytes = Int(
maxSizeInMB * 1024 * 1024
) // Convert MB to bytes
var compression: CGFloat = 1.0
let step: CGFloat = 0.1
var imageData = jpegData(compressionQuality: compression)
while (imageData?.count ?? 0) > maxSizeInBytes, compression > 0 {
compression -= step
imageData = jpegData(compressionQuality: compression)
}
return imageData
}
}
I'm calling a method with the context as parameter, within the context's perform block – is this really not legal in Swift 6?
actor MyActor {
func bar(context: NSManagedObjectContext) { /* some code */ }
func foo(context: NSManagedObjectContext) {
context.performAndWait {
self.bar(context: context)
// WARN: Sending 'context' risks causing data races; this is an error in the Swift 6 language mode
// 'self'-isolated 'context' is captured by a actor-isolated closure. actor-isolated uses in closure may race against later nonisolated uses
// Access can happen concurrently
}
}
}
The warning appears when I call a method with a context parameter, within the performAndWait-block.
Background: In my app I have methods that takes in API data, and I need to call the same methods from multiple places with the same context to store it, and I do not want to copy paste the code and have hundreds of lines of duplicate code.
Is there a well-known "this is how you should do it" for situations like this?
This is related to a previous post I made, but it's a bit flimsy and got no response: https://developer.apple.com/forums/thread/770605
I am adopting Swift Concurrency in my network extension app to use Swift 6 protections.
In the UI app I ended up with most of the app marked as MainActor, so that pieces of my app can keep seamless access to each other and at the same time have thread safe access.
When it comes to my network extension, does it make sense to also mark most of the code as MainActor for the purposes of thread safety and seamless access of most classes to each other? I have doubts, because MainActor sounds like it should be a UI think, but network extension has no UI
Of course any long or blocking operations would not be MainActor
Hi everyone,
I believe this should be a simple and expected default behavior in a real-world app, but I’m unable to make it work:
1. I have a View (a screen/page in this case) that calls an endpoint using async/await.
2. If the endpoint hasn’t finished, but I navigate forward to a DetailView, I want the endpoint to continue fetching data (i.e., inside the @StateObject ViewModel that the View owns). This way, when I go back, the View will have refreshed with the fetched data once it completes.
3. If the endpoint hasn’t finished and I navigate back to the previous screen, I want it to be canceled, and the @StateObject ViewModel should be deinitialized.
I can achieve 1 and 3 using the .task modifier, since it automatically cancels the asynchronous task when the view disappears:
view
.task { await vm.getData() }
I can achieve 1 and 2 using a structured Task in the View (or in the ViewModel, its the same behavior), for example:
.onFirstAppearOnly {
Task { away vm.getData() }
}
onFirstAppearOnly is a custom modifier that I have for calling onAppear only once in view lifecycle. Just to clarify, dont think that part is important for the purpose of the example
But the question is: How can I achieve all three behaviors? Is this really such an unusual requirement?
My minimum deployment target is iOS 15, and I’m using NavigationView + NavigationLink. However, I have also tried using NavigationStack + NavigationPath and still couldn’t get it to work.
Any help would be much appreciated.
Thank you, folks!
decidePolicyFor delegate method:
import WebKit
@objc extension DocumentationVC
{
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void)
Being called just alright in swift 5 minimal concurrency.
Raising concurrency to complete with swift 5 or swift 6. Changing the code to avoid warnings:
@preconcurrency import WebKit
@objc extension DocumentationVC
{
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
The delegate method is not being called. Changing back to swift 5 concurrency minimal - it is called.
Looking at WKNavigationDelegate:
WK_SWIFT_UI_ACTOR
@protocol WKNavigationDelegate <NSObject>
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(WK_SWIFT_UI_ACTOR void (^)(WKNavigationActionPolicy))decisionHandler WK_SWIFT_ASYNC(3);
Changing the delegate method to:
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping @MainActor (WKNavigationActionPolicy) -> Void) {
And it is called across swift 5 concurrency minimal to complete to swift 6.
I thought, the meaning of @preconcurrency import WebKit was to keep the delegate without @MainActor before the (WKNavigationActionPolicy) still matching regardless the swift concurrency mode?
My point is - this can introduce hidden breaking changes? I didn't see this documented anyhow at: https://www.swift.org/migration/documentation/migrationguide/.
decidePolicyFor is an optional method - so if signature 'mismatches' - there will be no warning on not-implementing the delegate method.
How do we catch or diagnose irregularities like this? Is it something @preconcurrency import WebKit should be ensuring and it is not?
Is this delegate mismatch a bug on swift side or something we should be taking care of while migrating? If it is on us, how do we diagnose these potential mismatches?
I've got a watch app, still with storyboard, WKInterfaceController and WatchConnectivity.
After updating it for swift 6 concurrency I thought I'd keep it for a little while without swift 6 concurrency dynamic runtime check.
So I added -disable-dynamic-actor-isolation in OTHER_SWIFT_FLAGS, but it doesn't seem to have an effect for the Apple Watch target. Without manually marking callbacks where needed with @Sendable in dynamic checks seem to be in place.
swiftc invocation is as (includes -disable-dynamic-actor-isolation):
swiftc -module-name GeoCameraWatchApp -Onone -enforce-exclusivity\=checked ... GeoCameraWatchApp.SwiftFileList -DDEBUG -enable-bridging-pch -disable-dynamic-actor-isolation -D DEBUG -enable-experimental-feature DebugDescriptionMacro -sdk /Applications/Xcode.app/Contents/Developer/Platforms/WatchOS.platform/Developer/SDKs/WatchOS11.2.sdk -target arm64_32-apple-watchos7.0 -g -module-cache-path /Users/stand/Library/Developer/Xcode/DerivedData/ModuleCache.noindex -Xfrontend -serialize-debugging-options -enable-testing -index-store-path /Users/stand/Library/Developer/Xcode/DerivedData/speedo-almhjmryctkitceaufvkvhkkfvdw/Index.noindex/DataStore -enable-experimental-feature OpaqueTypeErasure -Xcc -D_LIBCPP_HARDENING_MODE\=_LIBCPP_HARDENING_MODE_DEBUG -swift-version 6
...
-disable-dynamic-actor-isolation flag seems to be working for the iOS targets, I believe.
The flag is described here
Am I missing something? Should the flag work for both iOS and Apple Watch targets?
Hello. I am building an app that shows my walk workouts and in the detail view I want to show the route I took while walking, similar to that of the Apple Fitness App. There is a problem though, I cannot seem to understand how to connect the @State property workoutLocations array that would be used to draw the route on the map with what I get from the query. The task does successfully fetches the data but then when I try to use it later in a do-catch block nothing happens. What am I missing here?
import SwiftUI
import MapKit
import HealthKit
struct DetailView: View {
@Environment(HealthKitManager.self) var healthKitManager
let workout: HKWorkout
@State private var workoutLocations: [CLLocation] = []
var body: some View {
ScrollView {
//...
}
.task {
guard let store = self.healthKitManager.healthStore else {
fatalError("healthStore is nil. App is in invalid state.")
}
let walkingObjectQuery = HKQuery.predicateForObjects(from: workout)
let routeQuery = HKAnchoredObjectQueryDescriptor(predicates: [.workoutRoute(walkingObjectQuery)], anchor: nil)
let queryResults = routeQuery.results(for: store)
let task = Task {
var workoutRouteLocations: [CLLocation] = []
for try await result in queryResults {
let routeSamples = result.addedSamples
for routeSample in routeSamples {
let routeQueryDescriptor = HKWorkoutRouteQueryDescriptor(routeSample)
let locations = routeQueryDescriptor.results(for: store)
for try await location in locations {
workoutRouteLocations.append(location)
print(workoutRouteLocations.count) // this prints out the number of locations in the sample.
}
}
}
return workoutRouteLocations
}
do {
print(try await task.value.count) // this prints nothing. Therefore if I try to update workoutLocations array from here it would do nothing as well
// workoutLocations = try await task.value therefore does nothing and the array just doesn't get populated with the results of the task
} catch {
print(error)
}
}
}
}
Based on crash reports for our app in production, we're seeing these SwiftUI crashes but couldn't figure out why it is there. These crashes are pretty frequent (>20 crashed per day).
Would really appreciate it if anyone has any insight on why this happens. Based on the stacktrace, i can't really find anything that links back to our app (replaced with MyApp in the stacktrace).
Thank you in advance!
Crashed: com.apple.main-thread
0 libdispatch.dylib 0x39dcc _dispatch_semaphore_dispose.cold.1 + 40
1 libdispatch.dylib 0x4c1c _dispatch_semaphore_signal_slow + 82
2 libdispatch.dylib 0x2d30 _dispatch_dispose + 208
3 SwiftUICore 0x77f788 destroy for StoredLocationBase.Data + 64
4 libswiftCore.dylib 0x3b56fc swift_arrayDestroy + 196
5 libswiftCore.dylib 0x13a60 UnsafeMutablePointer.deinitialize(count:) + 40
6 SwiftUICore 0x95f374 AtomicBuffer.deinit + 124
7 SwiftUICore 0x95f39c AtomicBuffer.__deallocating_deinit + 16
8 libswiftCore.dylib 0x3d783c _swift_release_dealloc + 56
9 libswiftCore.dylib 0x3d8950 bool swift::RefCounts<swift::RefCountBitsT<(swift::RefCountInlinedness)1>>::doDecrementSlow<(swift::PerformDeinit)1>(swift::RefCountBitsT<(swift::RefCountInlinedness)1>, unsigned int) + 160
10 SwiftUICore 0x77e53c StoredLocation.deinit + 32
11 SwiftUICore 0x77e564 StoredLocation.__deallocating_deinit + 16
12 libswiftCore.dylib 0x3d783c _swift_release_dealloc + 56
13 libswiftCore.dylib 0x3d8950 bool swift::RefCounts<swift::RefCountBitsT<(swift::RefCountInlinedness)1>>::doDecrementSlow<(swift::PerformDeinit)1>(swift::RefCountBitsT<(swift::RefCountInlinedness)1>, unsigned int) + 160
14 MyApp 0x1673338 objectdestroyTm + 6922196
15 libswiftCore.dylib 0x3d783c _swift_release_dealloc + 56
16 libswiftCore.dylib 0x3d8950 bool swift::RefCounts<swift::RefCountBitsT<(swift::RefCountInlinedness)1>>::doDecrementSlow<(swift::PerformDeinit)1>(swift::RefCountBitsT<(swift::RefCountInlinedness)1>, unsigned int) + 160
17 SwiftUICore 0x650290 _AppearanceActionModifier.MergedBox.__deallocating_deinit + 32
18 libswiftCore.dylib 0x3d783c _swift_release_dealloc + 56
19 libswiftCore.dylib 0x3d8950 bool swift::RefCounts<swift::RefCountBitsT<(swift::RefCountInlinedness)1>>::doDecrementSlow<(swift::PerformDeinit)1>(swift::RefCountBitsT<(swift::RefCountInlinedness)1>, unsigned int) + 160
20 SwiftUICore 0x651b44 closure #1 in _AppearanceActionModifier.MergedBox.update()partial apply + 28
21 libswiftCore.dylib 0x3d783c _swift_release_dealloc + 56
22 libswiftCore.dylib 0x3d8950 bool swift::RefCounts<swift::RefCountBitsT<(swift::RefCountInlinedness)1>>::doDecrementSlow<(swift::PerformDeinit)1>(swift::RefCountBitsT<(swift::RefCountInlinedness)1>, unsigned int) + 160
23 libswiftCore.dylib 0x3b56fc swift_arrayDestroy + 196
24 libswiftCore.dylib 0xa2a54 _ContiguousArrayStorage.__deallocating_deinit + 96
25 libswiftCore.dylib 0x3d783c _swift_release_dealloc + 56
26 libswiftCore.dylib 0x3d8950 bool swift::RefCounts<swift::RefCountBitsT<(swift::RefCountInlinedness)1>>::doDecrementSlow<(swift::PerformDeinit)1>(swift::RefCountBitsT<(swift::RefCountInlinedness)1>, unsigned int) + 160
27 SwiftUICore 0x4a6c4c type metadata accessor for _ContiguousArrayStorage<CVarArg> + 120
28 libswiftCore.dylib 0x3d783c _swift_release_dealloc + 56
29 libswiftCore.dylib 0x3d8950 bool swift::RefCounts<swift::RefCountBitsT<(swift::RefCountInlinedness)1>>::doDecrementSlow<(swift::PerformDeinit)1>(swift::RefCountBitsT<(swift::RefCountInlinedness)1>, unsigned int) + 160
30 SwiftUICore 0x4a5d88 static Update.dispatchActions() + 1332
31 SwiftUICore 0xa0db28 closure #2 in closure #1 in ViewRendererHost.render(interval:updateDisplayList:targetTimestamp:) + 132
32 SwiftUICore 0xa0d928 closure #1 in ViewRendererHost.render(interval:updateDisplayList:targetTimestamp:) + 708
33 SwiftUICore 0xa0b0d4 ViewRendererHost.render(interval:updateDisplayList:targetTimestamp:) + 556
34 SwiftUI 0x8f1634 UIHostingViewBase.renderForPreferences(updateDisplayList:) + 168
35 SwiftUI 0x8f495c closure #1 in UIHostingViewBase.requestImmediateUpdate() + 72
36 SwiftUI 0xcc700 thunk for @escaping @callee_guaranteed () -> () + 36
37 libdispatch.dylib 0x2370 _dispatch_call_block_and_release + 32
38 libdispatch.dylib 0x40d0 _dispatch_client_callout + 20
39 libdispatch.dylib 0x129e0 _dispatch_main_queue_drain + 980
40 libdispatch.dylib 0x125fc _dispatch_main_queue_callback_4CF + 44
41 CoreFoundation 0x56204 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 16
42 CoreFoundation 0x53440 __CFRunLoopRun + 1996
43 CoreFoundation 0x52830 CFRunLoopRunSpecific + 588
44 GraphicsServices 0x11c4 GSEventRunModal + 164
45 UIKitCore 0x3d2eb0 -[UIApplication _run] + 816
46 UIKitCore 0x4815b4 UIApplicationMain + 340
47 SwiftUI 0x101f98 closure #1 in KitRendererCommon(_:) + 168
48 SwiftUI 0xe2664 runApp<A>(_:) + 100
49 SwiftUI 0xe5490 static App.main() + 180
50 MyApp 0x8a7828 main + 4340250664 (MyApp.swift:4340250664)
51 ??? 0x1ba496ec8 (Missing)
Based on the iPhone 14 Max camera, implement model recognition and draw a rectangular box around the recognized object. The width and height are calculated using LiDAR and displayed in centimeters on the real-time updated image.
Hi all, we try migrate project to Swift 6
Project use AVPlayer in MainActor
Selection audio and subtitiles not work
Task { @MainActor in let group = try await item.asset.loadMediaSelectionGroup(for: AVMediaCharacteristic.audible)
get error: Non-sendable type 'AVMediaSelectionGroup?' returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary
and second example
`if #available(iOS 15.0, *) {
player?.currentItem?.asset.loadMediaSelectionGroup(for: AVMediaCharacteristic.audible, completionHandler: { group, error in
if error != nil {
return
}
if let groupWrp = group {
DispatchQueue.main.async {
self.setupAudio(groupWrp, audio: audioLang)
}
}
})
}`
get error: Sending 'groupWrp' risks causing data races
Hello there,
I need to move through video loaded in an AVPlayer one frame at a time back or forth. For that I tried to use AVPlayerItem's method step(byCount:) and it works just fine.
However I need to know when stepping happened and as far as I observed it is not immediate using the method. If I check the currentTime() just after calling the method it's the same and if I do it slightly later (depending of the video itself) it shows the correct "jumped" time.
To achieve my goal I tried subclassing AVPlayerItem and implement my own async method utilizing NotificationCenter and the timeJumpedNotification assuming it would deliver it as the time actually jumps but it's not the case.
Here is my "stripped" and simplified version of the custom Player Item:
import AVFoundation
final class PlayerItem: AVPlayerItem {
private var jumpCompletion: ( (CMTime) -> () )?
override init(asset: AVAsset, automaticallyLoadedAssetKeys: [String]?) {
super .init(asset: asset, automaticallyLoadedAssetKeys: automaticallyLoadedAssetKeys)
NotificationCenter.default.addObserver(self, selector: #selector(timeDidChange(_:)), name: AVPlayerItem.timeJumpedNotification, object: self)
}
deinit {
NotificationCenter.default.removeObserver(self, name: AVPlayerItem.timeJumpedNotification, object: self)
jumpCompletion = nil
}
@discardableResult func step(by count: Int) async -> CMTime {
await withCheckedContinuation { continuation in
step(by: count) { time in
continuation.resume(returning: time)
}
}
}
func step(by count: Int, completion: @escaping ( (CMTime) -> () )) {
guard jumpCompletion == nil else {
completion(currentTime())
return
}
jumpCompletion = completion
step(byCount: count)
}
@objc private func timeDidChange(_ notification: Notification) {
switch notification.name {
case AVPlayerItem.timeJumpedNotification where notification.object as? AVPlayerItem [==](https://www.example.com/) self:
jumpCompletion?(currentTime())
jumpCompletion = nil
default: return
}
}
}
In short the notification never gets called thus the above is not working.
I guess the key there is that in the docs about the timeJumpedNotification: is said:
"A notification the system posts when a player item’s time changes discontinuously."
so the step(byCount:) is not considered as discontinuous operation and doesn't trigger it.
I'd be really helpful if somebody can help as I don't want to use seek(to:toleranceBefore:toleranceAfter:) mainly cause it's not accurate in terms of the exact next/previous frame as the video might have VFR and that causes repeating frames sometimes or even skipping one or another.
Thanks a lot
I'm trying to fix some Swift6 warnings, this one seems too strict, I'm not sure how to fix it. The variable path is a String, which should be immutable, it's a local variable and never used again inside of the function, but still Swift6 complains about it being a race condition, passing it to the task
What should I do here to fix the warning?
When using the continuation API, we're required to call resume exactly once. While withCheckedContinuation helps catch runtime issues during debugging, I'm looking for ways to catch such errors at compile time or through tools like Instruments.
Is there any tool or technique that can help enforce or detect this requirement more strictly than runtime checks? Or would creating custom abstractions around Continuation be the only option to ensure safety? Any suggestions or best practices are appreciated.
Hi everybody!
I'm desperately looking for help as I'm stuck with a rather fundamental problem regarding StoreKit2 - and maybe Swift Concurrency in general:
While renovating several freemium apps I'd like to move from local receipt validation with Receigen / OpenSSL to StoreKit2. These apps are using a dedicated "StoreManager" class which is encapsulating all App Store related operations like fetching products, performing purchases and listening on updates. For this purpose the StoreManager holds an array property with IDs of all purchased products, which is checked when a user invokes a premium function. This array can have various states during the app's life cycle:
Immediately after app launch (before the receipt / entitlements are checked) the array is empty
After checking the receipt the array holds all (locally registered) purchases
Later on it might change if an "Ask to Buy" purchase was approved or a purchase was performed
It is important that the array is instantly used in other (Objective-C) classes to reflect the "point in time" state of purchased products - basically acting like a cache: No async calls, completion handler, notification observer etc.
When moving to StoreKit2 the same logic applies, but the relevant API calls are (of course) in asynchronous functions: Transaction.updates triggers Transaction.currentEntitlements, which needs to update the array property. But Xcode 16 is raising a strict error because of potential data races when accessing the instance variable from an asynchronous function / actor.
What is the way to propagate IDs of purchased products app-wide without requiring every calling function as asynchronous? I'm sure I'm missing a general point with Swift Concurrency: Every example I found was working with call-backs / await, and although this talk of WWDC 2021 is addressing "protecting mutable states" I couldn't apply its outcomes to my problem. What am I missing?
I’m currently developing an iOS metronome app using DispatchSourceTimer as the timer. The interval is set very small, around 50 milliseconds, and I’m using CFAbsoluteTimeGetCurrent to calculate the elapsed time to ensure the beat is played within a ±0.003-second margin.
The problem is that once the app goes to the background, the timing becomes unstable—it slows down noticeably, then recovers after 1–2 seconds.
When coming back to the foreground, it suddenly speeds up, and again, it takes 1–2 seconds to return to normal. It feels like the app is randomly “powering off” and then “overclocking.” It’s super frustrating.
I’ve noticed that some metronome apps in the App Store have similar issues, but there’s one called “Professional Metronome” that’s rock solid with no such problems. What kind of magic are they using? Any experts out there who can help? Thanks in advance!
P.S. I’ve already enabled background audio permissions.
The professional metronome that has no issues: https://link.zhihu.com/?target=https%3A//apps.apple.com/cn/app/pro-metronome-%25E4%25B8%2593%25E4%25B8%259A%25E8%258A%2582%25E6%258B%258D%25E5%2599%25A8/id477960671
I'm trying to rewrite a Swift code to Swift 6 language mode and am stuck with this problem. How do I safely pass the bestAttemptContent and contentHandler to the Task? This is from the UNNotificationServiceExtension subclass.
final class NotificationService: UNNotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?
var customNotificationTask: Task<Void, Error>?
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
guard let bestAttemptContent = bestAttemptContent else {
invokeContentHandler(with: request.content)
return
}
do {
let notificationModel = try PushNotificationUserInfo(data: request.content.userInfo)
guard let templatedImageUrl = notificationModel.templatedImageUrlString,
let imageUrl = imageUrl(from: templatedImageUrl) else {
invokeContentHandler(with: bestAttemptContent)
return
}
setupCustomNotificationTask(
imageUrl: imageUrl,
bestAttemptContent: bestAttemptContent,
contentHandler: contentHandler
)
} catch {
invokeContentHandler(with: bestAttemptContent)
}
}
// More code
private func downloadImageTask(
imageUrl: URL,
bestAttemptContent: UNMutableNotificationContent,
contentHandler: @escaping (UNNotificationContent) -> Void
) {
self.customNotificationTask = Task {
let (location, _) = try await URLSession.shared.download(from: imageUrl)
let desiredLocation = URL(fileURLWithPath: "\(location.path)\(imageUrl.lastPathComponent)")
try FileManager.default.moveItem(at: location, to: desiredLocation)
let attachment = try UNNotificationAttachment(identifier: imageUrl.absoluteString, url: desiredLocation, options: nil)
bestAttemptContent.attachments = [attachment]
contentHandler(bestAttemptContent)
}
}
}
I tried using the MainActor.run {}, but it just moved the error to that run function.
The UNNotificationRequest is not sendable, and I don't think I can make it so.
Wrap the setupCustomNotification in a Task will move the errors to the didReceive method.
It seems like the consuming keyword will help here, but it leads to a compilation error, even with the latest Xcode (16.2).
Any pointers?
I am trying to migrate a WatchConnectivity App to Swift6 and I found an Issue with my replyHandler callback for sendMessageData.
I am wrapping sendMessageData in withCheckedThrowingContinuation, so that I can await the response of the reply. I then update a Main Actor ObservableObject that keeps track of the count of connections that have not replied yet, before returning the data using continuation.resume.
...
@preconcurrency import WatchConnectivity
actor ConnectivityManager: NSObject, WCSessionDelegate {
private var session: WCSession = .default
private let connectivityMetaInfoManager: ConnectivityMetaInfoManager
...
private func sendMessageData(_ data: Data) async throws -> Data? {
Logger.shared.debug("called on Thread \(Thread.current)")
await connectivityMetaInfoManager.increaseOpenSendConnectionsCount()
return try await withCheckedThrowingContinuation({
continuation in
self.session.sendMessageData(
data,
replyHandler: { data in
Task {
await self.connectivityMetaInfoManager
.decreaseOpenSendConnectionsCount()
}
continuation.resume(returning: data)
},
errorHandler: { (error) in
Task {
await self.connectivityMetaInfoManager
.decreaseOpenSendConnectionsCount()
}
continuation.resume(throwing: error)
}
)
})
}
Calling sendMessageData somehow causing the app to crash and display the debug message: Incorrect actor executor assumption.
The code runs on swift 5 with SWIFT_STRICT_CONCURRENCY = complete.
However when I switch to swift 6 the code crashes.
I rebuilt a simple version of the App. Adding bit by bit until I was able to cause the crash.
See Broken App
Awaiting sendMessageData and wrapping it in a task and adding the @Sendable attribute to continuation, solve the crash.
See Fixed App
But I do not understand why yet.
Is this intended behaviour?
Should the compiler warn you about this?
Is it a WatchConnectivity issue?
I initially posted on forums.swift.org, but was told to repost here.