Post not yet marked as solved
I've been trying to use the Xcode 14 beta and the -warn-concurrency flag to make some of our library code concurrency-safe, and I've hit just… too many problems to manage.
A smattering of random data points:
Foundation types that I think should be Sendable, are not:
URL
Notification
...
UIKit constants that should not be @MainActor, are:
UIApplication.willEnterForegroundNotification (it's just a string constant, and NotificationCenter itself is thread-safe)
UIKit types and methods that I think should not be @MainActor, are:
UIDevice, or at least I should be able to do UIDevice.current.systemVersion from any actor
Dispatch is completely concurrency-unaware — I kinda expected it to be unavailable, but instead it's available but doesn't understand swift concurrency
It'd at least be nice if DispatchQueue.main.[a]sync understood that its closure can be @MainActor (perhaps main could return a subclass of DispatchQueue with stronger guarantees?)
SwiftUI Button etc. callbacks aren't marked @MainActor, even though (AFAIK) they are — certainly it's always been legal to update @State vars from them — so it's not legal to call UIKit from them (eg. to present a UIViewController in response to a button press)
When can we expect Apple's SDKs to actually be usable with concurrency checking?
Post not yet marked as solved
In my app I am fetching RESTful data asynchronously from mutiple servers and merge the responses into an array when they are available:
let respModel = [responses]
I use didSet to check whether respModel is set and then trigger myTableView.reloadData() to udpate the table view each time
Sporadically, my app crashes during table view refresh and I think it's because two RESTful api calls arrive at exactly the same time, creating a deadlock on the respModel
I want to keep the current async functionality in, to get my table view updated whenever a fresh response arrives, but I also want to find a solution to the deadlock problem.
What's the best way to handle it?
Hi,
So I am using this function getListAtFIRStore() which is fetching data from firestore and returning it. But the fetching takes some time and before that happens the function already returns back the array which is empty at that moment.
How do I wait for the fetching task to be completed before the function returns the array back?
class CheckForDownloads {
var userName = String()
func refreshResources(forUser username: String) {
self.userName = username
let listOfImagesAtFirestore = getListAtFIRStore()
print(listOfImagesAtFirestore)
}
func getListAtFIRStore() -> [String]{
let root = Storage.storage().reference()
var str3 = [String]()
root.child("MagicFrame/\(userName)/images").listAll { (result, error) in
if let error = error {
print("Error in fetching list from firestore \(error.localizedDescription)")
}else{
for item in result.items{
if let str1 = (item.description.components(separatedBy: ["/"]).last) {
if let str2 = (str1.components(separatedBy: ["."])).first{
print(str2)
str3.append(str2)
}
}
}
}
}
return str3
}
}
Post not yet marked as solved
I have an ObservableObject which can do a CPU-bound heavy work:
import Foundation
import SwiftUI
@MainActor
final class Controller: ObservableObject {
@Published private(set) var isComputing: Bool = false
func compute() {
if isComputing { return }
Task {
heavyWork()
}
}
func heavyWork() {
isComputing = true
sleep(5)
isComputing = false
}
}
I use a Task to do the computation in background using the new concurrency features. This requires using the @MainActor attribute to ensure all UI updates (here tied to the isComputing property) are executed on the main actor.
I then have the following view which displays a counter and a button to launch the computation:
struct ContentView: View {
@StateObject private var controller: Controller
@State private var counter: Int = 0
init() {
_controller = StateObject(wrappedValue: Controller())
}
var body: some View {
VStack {
Text("Timer: \(counter)")
Button(controller.isComputing ? "Computing..." : "Compute") {
controller.compute()
}
.disabled(controller.isComputing)
}
.frame(width: 300, height: 200)
.task {
for _ in 0... {
try? await Task.sleep(nanoseconds: 1_000_000_000)
counter += 1
}
}
}
}
The problem is that the computation seems to block the entire UI: the counter freezes.
Why does the UI freeze and how to implement .compute() in such a way it does not block the UI updates?
What I tried
Making heavyWork and async method and scattering await Task.yield() every time a published property is updated seems to work but this is both cumbersome and error-prone. Also, it allows some UI updates but not between subsequent Task.yield() calls.
Removing the @MainActor attribute seems to work if we ignore the purple warnings saying that UI updates should be made on the main actor (not a valid solution though).
Wrapping every state update on a MainActor.run { } but this was too much boilerplate.
Post not yet marked as solved
Hi folks,
I am downloading some images from Firebase and adding them to main bundle resources, after which I use them.
I am having trouble with making my code to wait for the downloads to get complete before it executes the next statement.
I tried completion handlers, async/await but still the same.
Not sure if I am doing it the right way.
Can anyone help what is the correct approach for this scenario.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Create a session configuration
let configuration = ARImageTrackingConfiguration()
Task{
await self.checkForDownloads.refreshResources(forUser: self.username)
}
if let trackedImages = ARReferenceImage.referenceImages(inGroupNamed: "AR Resources", bundle: Bundle.main){
configuration.trackingImages = trackedImages
configuration.maximumNumberOfTrackedImages = 1
print("Images Found: \(trackedImages.count)")
}
sceneView.session.run(configuration)
}
func refreshResources(forUser username: String) async {
//..
}
I am expecting checkForDownloads.refreshResources to finish downloading first, then proceed to next statement.
Post not yet marked as solved
For some simulation work-loads I have, I would like to use the system to its full potential and therefore use both P and E cores. Splitting the work-load into individual tasks is not easily possible (the threads communicate with each other and run in semi-lockstep). I can allocate smaller portions of the domain to the E cores (and iteratively adjust this so they take the same amount of time as the P cores).
But in order for this to work well, I need to ensure that a given thread (with its associated workload) is bound to the right type of core: *either* the performance (doing larger chunks of the domain) or the efficiency (doing smaller chunks of the domain) cores.
What's the best way to do this? So far, I don't think thread-to-core affinity has been something that was choosable in macOS.
The documentation mentioned the QoS classes, but which class(es) (or relative priorities) would I pick?
c
pthread_set_qos_class_self_np(QOS_CLASS_UTILITY, 0);
The existing classifications don't really map well, the work is user-initiated (i.e. they launched a console application), but not a GUI program. Would I use 4 threads with QOS_CLASS_UTILITY and 4 with QOS_CLASS_BACKGROUND? Would I just use UTILITY with relative priority for performance vs. efficiency cores?
I am creating a Safari App Extension for users to save web links and quotes to my app. I would like to cache web page metadata when links are saved by the extension.
I am using a context menu command to capture information with the code below.
override func contextMenuItemSelected(
withCommand command: String,
in page: SFSafariPage,
userInfo: [String : Any]? = nil)
{
switch command {
case "sendToApp":
Task {
guard let properties = await page.properties(),
let pageURL = properties.url else {
return
}
let provider = LPMetadataProvider()
provider.timeout = 0.01
os_log("*** Starting metadata call ***")
let metadata = try? await provider.startFetchingMetadata(for: pageURL)
os_log("*** Continued past metadata call ***")
// ...
}
}
I get the log:
*** Starting metadata call ***
LPMetadataProvider<1>: start fetching for URL
...but I am never seeing the log "*** Continued past metadata call ***"
I wonder if the task is being killed for some reason?
I thought maybe async code was an issue in SFSafariExtensionHandler, but the first await call in the guard passes successfully.
I thought that the default timeout of 30s on LPMetadataProvider may be too great, but it still fails with a tiny timeout of 0.01s.
I have added com.apple.security.network.client to the entitlements of the extension.
Is there something I am missing please?
Post not yet marked as solved
I am using a database with NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
Everything works fine, I save objects, read, and delete without problems.
If set "-com.apple.CoreData.ConcurrencyDebug 1" :
I save the object through the PerformAndWait block - ok
I do fetch - receive an object, but its properties are empty (data = fault) and it crashes. (EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0))
sample code:
public final class DatabaseCore {
private let persistentContainer: NSPersistentContainer
private var backgroundContext: NSManagedObjectContext!
private let queue: DispatchQueue
public init() {
self.persistentContainer = Self.createPersistentContainer()
self.queue = queue
self.queue.async {
self.backgroundContext = Self.createNewBackgroundContext(container: self.persistentContainer)
}
// Private
private static func createPersistentContainer() -> NSPersistentContainer {
let model = NSManagedObjectModel(contentsOf: Bundle.module.url(forResource: "TestModel", withExtension: "momd")!)
let container = NSPersistentContainer(name: "TestModel", managedObjectModel: model)
let description = NSPersistentStoreDescription()
description.url = URL(fileURLWithPath: "/dev/null")
container.persistentStoreDescriptions = [description]
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// print error
}
})
return container
}
private static func createNewBackgroundContext(container: NSPersistentContainer) -> NSManagedObjectContext {
let context = container.newBackgroundContext()
context.mergePolicy = NSOverwriteMergePolicy
context.undoManager = nil
return context
}
private static func saveContext(_ context: NSManagedObjectContext) {
context.performAndWait {
if !context.hasChanges { return }
do {
try context.save()
} catch {
// print error
}
}
}
// Public
public func upsert(_ block: @escaping (NSManagedObjectContext) -> Void, completion: (() -> Void)?) {
self.queue.async {
let context: NSManagedObjectContext! = self.backgroundContext
self.backgroundContext.performAndWait {
block(context)
Self.saveContext(context)
}
completion?()
}
}
public func fetch<ResultType: NSFetchRequestResult>(_ request: NSFetchRequest<ResultType>, completion: @escaping ([ResultType]) -> Void) {
self.queue.async {
var result: [ResultType] = []
self.backgroundContext.performAndWait {
do {
result = try request.execute()
} catch let error {
// print error
}
}
completion(result)
}
}
}
I set a breakpoint to result fetch func
if set "-com.apple.CoreData.ConcurrencyDebug 1":
(lldb) po record
<Test: 0x7b1400030070> (entity: Test; id: 0xbb200e8d680f1045 <x-coredata://1BFC3A95-3F7D-4A23-AD20-FDF23B575D73/CDLog/p1>; data: <fault>)
(lldb) po record.value
error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).
The process has been returned to the state before expression evaluation.
Without "-com.apple.CoreData.ConcurrencyDebug 1",
everything works correctly, without a crash.
(lldb) po record
<Test: 0x7b1400028c80> (entity: Test; id: 0xb29880bd32a57757 <x-coredata://1BFC3A95-3F7D-4A23-AD20-FDF23B575D73/CDLog/p1>; data: <fault>)
(lldb) po record.value
0
How can this be fixed?
Thank you! =)
Xcode 13.3
Hi,
I wish to remotely load some data before I initialise the collection view. I read somewhere one way to do this is to only set the delegate and datasource after I loaded the data.
However I'm setting the error
"UICollectionView.delegate must be used from main thread only"
next to the line
self.myCollectionView.delegate = self
I suppose this is not the best way to do this. What is the recommended way?
Cheers
Richard
Post not yet marked as solved
I've searched the web on this topic and can't find good info.
I have a device (an ESP32 Programmable System on a Chip) and I'd like to sync its clock to that of the iPhone. (I want to sync the clocks so that I correctly fuse/align data collected by the ESP32 with GPS location data from the iPhone GPS receiver.) My current approach is to use NTP to sync the ESP32's clock to "network time" (available from time.apple.com) just like (I believe) the iPhone is doing. But, I would have to test the accuracy of this method as I'd __like to __ achieve an accuracy on the order of 0.01 seconds. (FYI: the ESP32 ecosystem provides NTP functionality.)
I've considered syncing the ESP32 clock to GPS time (via an external GPS receiver) but I am told that GPS time is not available on IOS. Thus, I cannot use GPS time to correctly align/fuse measurement from the ESP32 with the iPhone.
Thanks.
Hi eskimo, team.
I have a situation where my app is crashing on startup only for devices using iOS 12.x. I was able to manage to get the crash log from a device that I have however the log is not friendly at all.
Can anybody please shed some light? Ty
Crash stack
I'm trying to hint the task scheduler that some threads should be scheduled together using the task_policy_set API with THREAD_AFFINITY_POLICY (in lieu of there being no "real" thread to core affinity API).
All the examples mention setting the policy after creation but before execution of the task(s). Unfortunately, I'm not creating these tasks (but OpenMP is), and when I then try to use the API on an already running thread, I get a return value of KERN_INVALID_ARGUMENT(= 4)
thread_affinity_policy_data_t policy = { 1 };
auto r = thread_policy_set(mach_task_self(), THREAD_AFFINITY_POLICY, (thread_policy_t)&policy, THREAD_AFFINITY_POLICY_COUNT);
When I replace mach_task_self() by pthread_mach_thread_np(pthread_self()), I get an KERN_NOT_SUPPORTED error instead (= 46, "Empty thread activation (No thread linked to it)").
Has anyone used these APIs successfully on an already running thread?
Background:
The code I'm working on divides a problem set into a small number of roughly equal sized pieces (e.g. 8 or 16, this is an input parameter derived from the number of cores to be utilized). These pieces are not entirely independent but need to be processed in lock-step (as occasionally data from neighboring pieces is accessed).
Sometimes when a neighboring piece isn't ready yet for a fairly long time, we call std::this_thread::yield() which unfortunately seems to indicate to the scheduler that this thread should move to the efficiency cores (which then wreaks havoc with the assumption of each computation over a piece roughly requiring the same amount of time so all threads can remain in lock-step). :(
A similar (?) problem seems to happen with OpenMP barriers, which have terrible performance on the M1 Ultra at least unless KMP_USE_YIELD=0 is used (for the OpenMP run-time from LLVM). Can this automatic migration (note: not the relinquishing of the remaining time-slice) be prevented?
Post not yet marked as solved
I’m seeing a crash that looks to be related to TaskGroup from Swift Concurrency. I can’t catch it in the debugger, only when running on device. It looks like the crash is actually in the Swift Concurrency library, and I’m not sure how to trace from there back to something in my own code...
Some digging so far suggests the issue only happens:
on iOS 15.0->15.3.x (ie. it looks to not happen on 15.4)
on older iPhones such as 6s, 6s plus, 7, 8, X
Where do I start trying to figure out the root cause of this?
Crash Report
Post not yet marked as solved
I'm trying to figure out what @MainActor means in this definition of UICloudSharingController in Apple Documents found here.
Here's what it says:
@MainActor class UICloudSharingController : UIViewController
I couldn't find much information on Google. This Apple Documentation is the closest thing I found. Is this what i need? Here is the declaration to the MainActor class:
@globalActor final actor MainActor
Post not yet marked as solved
I'm writing an app that performs actions in a web browser automatically on user's behalf. Currently my app uses OperationQueue to perform asynchronous work. I'm trying to rewrite the app to use async/await instead. Note, that it is necessary for my app to limit the number of concurrently executing work items. Otherwise the app may run into server throttling issues.
However, I can't find an equivalent to OperationQueue's maxConcurrentOperationCount parameter in the new concurrency model. Is there a way to limit the number of concurrently running "lanes" of execution without resorting to using OperationQueue / DispatchQueue / NSLock ?
Post not yet marked as solved
I'm trying to test an API I'm writing with Structured Concurrency. I prefer to run my tests with continueAfterFailure = false so that I may establish preconditions on my tests. At the moment my test is:
func testRssUrl() async throws {
self.continueAfterFailure = false
let xml = PodcastXml.url(url)
let feed = try await xml.feed
let rssFeed: RSSFeed? = feed.rssFeed
XCTAssertNil(rssFeed) // Included for this sample
XCTAssertNotNil(rssFeed)
validate(zebraFeed: rssFeed!)
}
My expectation of Swift Concurrency is that the try await should hold the method, until xml.feed resolves to a value or throws. Either of the XCTAssertNil or XCTAssertNotNil should fail (in the actual test, I'm not using NotNil). As written, between the two asserts and the try, validate(zebraFeed: ) should never be called because of continueAfterFailure.
Yet it is. The test still fails because XCTAssertNil is failing, which is my actual expectation. Test execution should also be stopping there.
What am I missing?
Post not yet marked as solved
When marking the ViewController and the function with @MainActor, the assertion to check that the UI is updated on main thread fails.
How do I guarantee that a function is run on Main Thread when using @MainActor?
Example code:
import UIKit
@MainActor
class ViewController: UIViewController {
let updateObject = UpdateObject()
override func viewDidLoad() {
super.viewDidLoad()
updateObject.fetchSomeData { [weak self] _ in
self?.updateSomeUI()
}
}
@MainActor
func updateSomeUI() {
assert(Thread.isMainThread) // Assertion failed!
}
}
class UpdateObject {
func fetchSomeData(completion: @escaping (_ success: Bool) -> Void) {
DispatchQueue.global().async {
completion(true)
}
}
}
Even changing DispatchQueue.global().async to Task.detached does not work.
Tested with Xcode 13.2.1 and Xcode 13.3 RC
Hi,
I'm not sure if this is intended behaviour but I run into a segfault when running the following code on XCode 13.2.1 on MacOS 11 (works fine on 12)
import Foundation
print("Hello, World!")
func runAsync() async {
print("async!")
}
Task {
await runAsync()
}
sleep(5)
Segfault:
#0 0x00007fff2cf09dc7 in swift::ResolveAsSymbolicReference::operator()(swift::Demangle::__runtime::SymbolicReferenceKind, swift::Demangle::__runtime::Directness, int, void const*) ()
I saw a similar thread related to https://forums.swift.org/t/async-await-crash-on-ios14-with-xcode-13-2-1/54541/12 but using XCode 13.3 beta didn't solve my problem.
Is this supposed to not work? My understanding was that backporting concurrency was now supported by linking to libswift_Concurrency.
I tried optionally linking to this as build step but that didn't help either.
PS I created my project just using the template console app project for mac os in XCode
Post not yet marked as solved
We noticed when using async await with SpriteKit run function.
Self is retained until run action is completed which can cause memory leak.
Task {
try? await Task.sleep(nanoseconds: 2 * NSEC_PER_SEC)
await controller.startIntro()
print("intro done")
}
If you deallocate controller, memory won't be released until await is not completed.
calling:
scene.removeAllActions()
cause memory leak.
Here is project reproducing steps with possible workarounds:
https://github.com/maradic/SpriteKitConcurrencyBug
Is this SpriteKit bug or am I doing something wrong?
Post not yet marked as solved
I have a macOS app that has a minimal deployment target of macOS 10.15, recently I adapted some features with Swift Concurrency.
I built my app with Xcode 13.2 then upload to App Store Connect, but it failed.
App Store Connect reported "This bundle is invalid.", which "libswift_Concurrency.dylib" is unsupported content.