This is true. But in my case, the older APIs are Apple's. Suppose the app you're building uses common frameworks like Bluetooth, Vision, or AVFoundation. You have a choice:
You can write old code that's immediately outdated and will require a huge refactor in a few years. DispatchQueue, delegates, etc.
You can fight Swift and attempt to shoehorn older frameworks into modern Swift concurrency. AsyncSequence, Tasks, Actors, etc.
But this is a damned-if-you-do-damned-if-you-don't situation. In #1, you can't really go back to a Client and say, "Hey, rememember how we wrote all that code two years ago and you paid a bunch of money for it? Right. So I want to re-do all of that. Your app gets no new features. It won't be faster. It'll cost you $60,000. Sound good?" So you're stuck carrying ancient code forever.
In #2, your productivity nosedives because you waste HOURS of your life googling how to get around some dumb compiler error that's not REALLY an error outside of an academic course on compilers. So you make very little forward progress and you're still writing code that's not futureproof because when Apple does get around to updating their frameworks with modern APIs, you're hosed.
All the WWDC demos show concurrency as if it's ready-to-go and the best practice for building apps. But the reality is...not that.
The language support for Concurrency might be kinda-sorta mostly almost done. But that's only 50% of the story. The other half was auditing Apple's frameworks to make sure they're ready for concurrency. That ball got dropped. So I have an NSTableColumn that isn't assigned to MainActor and there's no way for me to fix Apple's oversight (other than cumbersome workarounds).
Why don't you use an older compiler for older code though? I honestly don't understand. You are trying to upgrade your older code to an unreleased beta version of the compiler, but why even bother?
(Somewhat reminds of Python 2 -> 3 situation, just don't upgrade if unsure)
I see this issue quite often, and I think the reason here is that nobody updates the code iteratively. Itâs always two extremes: you all old, rewrite to new stopping the work. While in reality there is middle ground, when you do the feature or fix a bug, you also update your code â improve a bit this and there, use new language feature is makes sense, etc. In that way, over time the code is stays relatively up-to-date with the latest features of the language and platform, and only gets improved. I see how in many projects currently is still completely ignored strict checking of concurrency that has been available for a few years now, while these projects use new concurrency. If those checks were on and app has been progressively migrated, number of issues that require fixing with Swift 6 would be a few orders smaller.
You can write old code that's immediately outdated and will require a huge refactor in a few years. DispatchQueue, delegates, etc.
I don't think this is correct. Regardless of all the efforts put into Swift concurrency, there has been no indication that libdispatch is going anywhere for the foreseeable future.
Not only that. There are also 3rd party libraries and frameworks that are well established/debugged/widely used. And it looks like the new Swift concurrency makes them incompatible or obsolete. And I doubt that their respective authors will rewrite them at once. Bad luck if your app uses one of them.
I might be missing something, but it looks to me like many people believe that they have to switch to the Swift 6 language mode now. You can continue using the Swift 5 language mode of the Swift 6 compiler in Xcode 15 and ignore the new concurrency checking, while still using all the other features of Swift 6. The Swift 5 mode will be supported for a long time (Swift 6 compiler still supports the Swift 4.2 language mode which might indicate the time horizon here).
I did not try to use the Swift 6 myself (I usually avoid using beta releases), but from other people's comments it looks like the Swift 5 language mode introduces a large number of warnings, unlike "real" Swift 5. I can only hope that in the final release of Swift 6 Swift developers will make Swift 5 language mode an equivalent of Swift 5 in regard to warnings and errors.
I don't know of any such issue that hasn't already been fixed, and if anybody knows of one, please file it on GitHub. There are concurrency warnings in the Swift 5 language mode in minimal concurrency checking in places where you are explicitly using actors or async/await, etc, but those are not new in the Swift 6.0 compiler.
This is a huge exaggeration. One of my clients has a couple heavily used custom apps written in Objective C. One of them was mostly written in 2005 or so. I've added quite a few features over the years, it still runs well on macOS 14, and it even imports a couple swift frameworks I wrote. Another app was written in the early 2000s, and I rewrote that one in 2012 with quite a bit of refactoring. Both of those apps have needed some work over the years to handle deprecated and removed APIs. Is it kind of annoying to go work on an Objective-C codebase these days? It is at least a little nostalgic, especially when a nil sneaks through to somewhere it shouldn't and crashes the app. But the apps still work great, and are at no real risk of not being able to be compiled at least for the foreseeable future. The age of the code often doesn't really matter that much.
I've also written a couple of Objective-C iOS apps in the 2010 timeframe that have been re-written with Swift/SwiftUI in the past few years. And guess what? Re-writing a relatively small iOS app in Swift doesn't really take that long. They're also more than 2 years old now, so in your view I guess there's no point to make small updates, they're due for a complete rewrite for reasons. I probably won't get around to that until 2030 at least but they'll be updated every couple years with new features.
In any case, I've found your problem. But if your client is good with giving you another $60K just because you feel the code could be better, well, good for you I guess.
This is true and even for not-so small apps too. Mobile apps are inherently limited in functionality (if you forget about games and a few other niches for a moment), they can only do so much due to the small screen size and limited interaction. Even on the iPad where interaction can be a bit more complex, you don't get to write a Logic Pro or even a GarageBand too often.
So yes, I can confirm from my experience too, rewriting a mobile app is easy and usually takes from weeks to a few months to have a functional product fully equivalent to its predecessor.
That were a completely false statements from the others. While you can find a lot simple apps that can be made over a weekend there, there are a lot of a complex apps as well. I donât have any case when app with a long history couldâve been rewritten in a few months. That simply impossible given that over years it more likely to gain tons of details you have to address. But even app that were developer for a year has tons of things inside that you just physically (you cannot type and think that fast!) wouldnât be able to write from the scratch even if we put aside tons of details.
To @jimc: in my experience over ten years of development for iOS, I have seen an app being rewritten only once and it took more than a year for a group of a skilled developers. I have heard a lot of being able to replicate some complex app over weekends, but havenât seen that being done. The amount of complexity and features that being put into apps is just beyond ability of a person to handle as a whole, so if anyone thinks that this is possible to rewrite app completely in a month or two, have either an extremely simple app or just wrong in their evaluation.
@hborla Did that fix make it into the Swift version bundled with Xcode 16 Beta 6? I'm still getting tons of nonsense errors in Beta 6 that don't show up in Beta 4. This is in Swift 5 language mode.
@hborla I see. So is the above behavior a new, unknown bug? The class is assigned to MainActor and the compiler is complaining that -awakeFromNib is a nonisolated context.
Is -awakeFromNib somehow special now? Why does the compiler think it's not isolated to the MainActor? I guess the fundamental question is: should I file a Radar about this, or am I just a moron who's not using Swift Concurrency correctly? (I'm willing to err on the side of the latter, given how utterly complex Swift Concurrency has turned out to be.)
It's "behaving as designed", but the design of this rule is not very easy to use with Objective-C subclassing patterns where you have a root class for all classes and you need some subclasses to be isolated to the main actor. NSObject.awakeFromNib is not special - the original bug that I fixed which exposed these errors is because these rules were originally accidentally ignored when subclassing something imported from Objective-C. The reason this is an error is because awakeFromNib is allowed to be called from off of the main actor, but the override is accessing main-actor-isolated state. Here's a (very contrived) small example:
NSObject itself is not isolated to the main actor, and neither is awakeFromNib(). You're allowed to call this method from off of the main actor. You're also allowed to have an instance of UIViewController from outside the main actor. Its mutable state is protected by actor isolation, so it's safe to pass a reference around, as long as you get back on the main actor to access its mutable state.
I special cased NSObject.init in my fix for release/6.0 because in many cases, the error is impossible to workaround, and it's also very difficult to actually get into a situation where you have a runtime data race by calling your subclass initializer override through the NSObject metatype.
You don't need to file a radar - I know this is a usability issue with the Swift 6 data-race safety rules (including Swift 5 mode with complete concurrency checking), and various issues have already been filed on GitHub about this specific problem. I think there are a number of different options for drastically improving the usability here, and I need to think through the implications of each option, etc. This is design work, not a bug fix, and it will not be fixed for the Swift 6.0 release.
So what would you suggest as the optimal hack to use until Apple figures this out? MainActor.assumeIsolated inside of -awakeFromNib()? A MainActor.run{} Task inside -awakeFromNib()?
I can't believe Apple is willing to just let developers collectively encounter billions of error messages in -awakeFromNib() with no guidance on how to handle them. I get that SwiftUI is the future, but Nibs power millions of apps and they are about as common as it getsâthey aren't a "little edge case".
Is this really true, like, at an API contract level? It's been a while since I worked heavily with XIB files but I never understood it to be invalid to manipulate UI-thread stuff from within awakeFromNib on a UIView or UIViewController subclass.
Or, I suppose another way of asking the question: if the body of awakeFromNib is wrapped in MainActor.assumeIsolated, will that assumption be violated in any circumstances other than user code deliberately calling awakeFromNib off the main thread?
(I realize you might not be the best person to answer this but if any AppKit/UIKit gurus are able to answer that would be lovely.)
@Jumhyn It's sort of a "what is technically allowed" versus "what is done" thing. There were some esoteric cases 20 years agoâwhen Macs were far less powerfulâwhere you might load a complex Nib in the background. But I've never seen AppKit classes (NSWindowController, NSViewController, etc.) use anything other than the main thread.