Discuss Swift.

Swift Documentation

Post

Replies

Boosts

Views

Activity

macOS 15 + Xcode 16 Beta 4 Problem with .task {} and async function
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() }
1
0
598
Jul ’24
Main actor-isolated instance method 'locationManagerDidChangeAuthorization' cannot be used to satisfy nonisolated protocol requirement
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
4
0
2.2k
Jul ’24
Implementing a Main Actor Protocol That’s Not @MainActor
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.
0
0
837
Jul ’24
Unable to import ObjC interface from a package in a public ObjC file in a framework
Problem Statement: Unable to import .h file from an ObjC SPM to a .h file in an ObjC file in a framework with mix of ObjC and Swift code The issue is: in order to support access of ObjC file in Swift code in a framework we need to use umbrella header (in place of bridging header). Once the file is imported in Umbrella header and made public it will no longer allow import of .h file from package in its interface Project Structure: a. Package: ObjCPackage ObjCPackage |- Package.swift |- ObjCPackage MathsUtilities.h (class interface) MathsUtilities.m (class implementation) NiceLogs.h (protocol) b. Project: ObjCSwiftFramework ObjCSwiftFramework |- ObjCSwiftFramework.h (umbrella header) |- Calculation.h (objc class interface) |- Calculation.m (objc class implementation) |- SwiftCalci.swift (swift class) Details: #import <ObjCSwiftFramework/Calculation.h> added in ObjCSwiftFramework.h as Calculation has to be used in SwiftCalci Calculation.h marked as public in target membership in Xcode so that it can be added in umbrella header ObjCSwiftFramework.h #import "NiceLogs.h" in Calculation.h gives error Here is a small sample which I created to demonstrate the problem: ObjCSwiftFramework
1
0
448
May ’24
NumberFormat formatting exceeds 16 decimal places exception
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?
1
0
316
Jul ’24
Weird crashes when accessing Swift Array
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)?
13
0
1.1k
Jul ’24
[SwiftUI] When to use closures vs equals for variable assignment?
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?
1
0
366
Jul ’24
How to use network sockets with async/await?
I have an application that communicates with custom external hardware on the network (using UDP). I have a thread that receives and process the UDP data and then signals a waiting thread by releasing a semaphore when data is available. A have a asyncSendAndReceive and asyncReceive function that just begs to use async/await. But I cannot simply switch because of the use of the semaphore. Various forums and discussions said that semaphores should no longer be used for signalling. If not semaphores, then what else? Note that my two async functions may not always block. If data was received before they were called, then it is queued (and the semaphore is signalled).
9
0
3k
Nov ’22
Model Container Sendable Throwing Error in Swift 6
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.
3
1
983
Jul ’24
Contents of Swift dictionaries and arrays being lost
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.
3
0
444
Jul ’24
Swift app is failing to build with Xcode 15.4 on Sonoma Silicon
HI, I have a app with Swift Programming language. It is built successfully on my Monterey Intel by using Xcode 14. I am trying to built same app on Sonoma Silicon arm64 by using Xcode 15.4. But app is failing to build with below errors. Can anyone suggest reason for this? Copy /Users/testuser/git/agent/out/Darwin/Release/TESTFileProvider.appex/Contents/Resources/swift-nio__NIOFileSystem.bundle /Users/testuser/git/agent/out/Darwin/Release/swift-nio__NIOFileSystem.bundle (in target 'TESTFileProvider' from project 'TEST') cd /Users/testuser/git/agent/dgagent/agent/macosx/dgc/TEST builtin-copy -exclude .DS_Store -exclude CVS -exclude .svn -exclude .git -exclude .hg -resolve-src-symlinks /Users/testuser/git/agent/out/Darwin/Release/swift-nio__NIOFileSystem.bundle /Users/testuser/git/agent/out/Darwin/Release/TESTFileProvider.appex/Contents/Resources error: /Users/testuser/git/agent/out/Darwin/Release/swift-nio__NIOFileSystem.bundle: No such file or directory (in target 'TESTFileProvider' from project 'TEST') Copy /Users/testuser/git/agent/out/Darwin/Release/TESTFileProvider.appex/Contents/Resources/swift-nio_NIOPosix.bundle /Users/testuser/git/agent/out/Darwin/Release/swift-nio_NIOPosix.bundle (in target 'TESTFileProvider' from project 'TEST') cd /Users/testuser/git/agent/dgagent/agent/macosx/dgc/TEST builtin-copy -exclude .DS_Store -exclude CVS -exclude .svn -exclude .git -exclude .hg -resolve-src-symlinks /Users/testuser/git/agent/out/Darwin/Release/swift-nio_NIOPosix.bundle /Users/testuser/git/agent/out/Darwin/Release/TESTFileProvider.appex/Contents/Resources error: /Users/testuser/git/agent/out/Darwin/Release/swift-nio_NIOPosix.bundle: No such file or directory (in target 'TESTFileProvider' from project 'TEST')
3
0
477
Jul ’24
Why use async/await vs completion handlers?
I'm pretty sure I'm missing something completely obvious, but I can't see what. I watched WWDC session, read the Swift evolution blog, and they all make sense, but still it doesn't click for me. Please help me out here :) . I'm diving into adopting the 'new' async/await style of coding (I know, it's old news at this point, but I could only get to it now), and so I'm all pumped to get my code to go eleven and therefore I wrote a small data-downloader class. It has one method, well two: one oldskool function with a completionHandler, and one new style async/await one. When using the oldskool one, it works as everyone would expect: print(1) dataFetcher.fetchSomeData { print(2) let data = $0 // process data ... print(3) } print(4) The output is, unsurprisingly: 1 4 2 3 Now, when I use my new style function: let data = await dataFetcher.fetchSomeData() // process data ... Xcode gives me an error: 'async' call in a function that does not support concurrency That makes sense, I am calling this in the viewDidLoad() method of a UIViewController subclass. Can't mark viewDidLoad() as await, as super's implementation is not async. No problem, let me wrap it in Task: print(1) Task { print(2) let data = await dataFetcher.fetchSomeData() // process data ... print(3) } print(4) No errors, and this code works exactly as expected, the output is: 1 4 2 3 So now I am wondering: why take the effort of changing/adding code for async/await style function that ultimately end up requiring exactly the same amount of code, and is exactly as non-linear as completionHandlers? Note that the dataFetcher only has one property, an instance ofURLSession, so I am also not even managing my own queues or threads in the oldskool method vs the new one. They just wrap URLSession's functions: func dataTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask and func download(for request: URLRequest, delegate: URLSessionTaskDelegate? = nil) async throws -> (URL, URLResponse) Is async/await useful only with SwiftUI maybe? What am I missing here? Please help me see the light.
8
0
7.2k
Aug ’22
Need Objective-C translation of DispatchSource.makeFileSystemObjectSource
I came across a useful repo on GitHub: https://github.com/GianniCarlo/DirectoryWatcher/blob/master/Sources/DirectoryWatcher/DirectoryWatcher.swift self.queue = DispatchQueue.global() self.source = DispatchSource.makeFileSystemObjectSource(fileDescriptor: descriptor, eventMask: .write, queue: self.queue) self.source?.setEventHandler { [weak self] in self?.directoryDidChange() } self.source?.setCancelHandler() { close(descriptor) } self.source?.resume() How do I translate this to OC version? I have an app that was written in OC and I plan to incorporate this directory watcher into the project.
2
0
542
Jul ’24
What does ".island" suffix in symbol name mean?
During my analysis of the binary size changes after compiling Swift source code, I discovered symbols with the ".island" suffix. I couldn't find meaningful information about this suffix through my search, so I decided to reach out for assistance. While comparing the changes in binary size after modifying specific code, I noticed a significant increase (from 33MB to 520MB). Upon analyzing the symbols of the enlarged binary using the nm command, I found the following pattern: t _$s12{SomeSymbol}WOb t _$s12{SomeSymbol}WOb.island t _$s12{SomeSymbol}WOb.island2 t _$s12{SomeSymbol}WOb.island3 When I output the symbols of the binary using nm, I noticed many symbols with the same name but different ".island", ".island2", ".island3" suffixes. Disassembling the binary showed that functions with these suffixes simply delegate calls sequentially: x.island3 -> x.island2 -> x.island1 -> x. It appears that these symbols serve as delegates for function calls, but I would like to understand why such duplicated functions with these suffixes are generated. Could someone help me to provide some insights on this matter?
1
0
428
Jul ’24