TipKit vs. Swift 6 + Concurrency

I'm trying to convert my project to use Swift 6 with Complete Concurrency in Xcode 16 beta 1.

The project uses TipKit, but I'm getting compile errors when trying to use the TipKit Parameters feature. Here is an example of the type of error I'm seeing (Note that this code from https://developer.apple.com/documentation/tipkit/highlightingappfeatureswithtipkit):

struct ParameterRuleTip: Tip {
    // Define the app state you want to track.
    @Parameter
    static var isLoggedIn: Bool = false
Static property '$isLoggedIn' is not concurrency-safe because it is non-isolated global shared mutable state.

Is there a new pattern for supporting TipKit Parameters in Swift 6 with Complete Concurrency enabled? There is no obvious suggestion for how to fix this.

The latest WWDC 2024 TipKit doesn't appear to have any solution(s).

Answered by DTS Engineer in 791438022

@dmcgloin Thanks for flagging that. Could you please file a bug report and post the FB number here once you do. Please be sure to test on future beta releases as well.

@dmcgloin Thanks for flagging that. Could you please file a bug report and post the FB number here once you do. Please be sure to test on future beta releases as well.

Submitted bug report: FB13946094

FYI: Also posted question to Stack Overflow here: https://stackoverflow.com/questions/78630356/tipkit-parameters-incompatible-with-swift-6-concurrency-property-is-not-concurr

I am experiencing this issue as well. It appears to happen if the Tip resides in a framework. Here's my own feedback: FB14037329

@dmcgloin Thank you for flagging this, we should have the macro expansion updated soon, in the interm you can write the manual expansion of the @Parameter macro and include nonisolated(unsafe) as a workaround.

struct ParameterRuleTip: Tip {
    // Define the app state you want to track.
static var isLoggedIn: Bool = false {
        get {
            $isLoggedIn.wrappedValue
        }
        set {
            $isLoggedIn.wrappedValue = newValue
        }
    }

    static nonisolated(unsafe) var $isLoggedIn: Tips.Parameter<Bool> = Tips.Parameter(Self.self, "+isLoggedIn", true)

TipKit handles thread safety for Parameter's internally so marking it as nonisolated(unsafe) is totally okay!

Thanks. Glad to know that a solution is on the way. FWIW, I tried to use the suggested workaround and got the following compile errors:

"Variable with getter/setter cannot have an initial value" (for setting isLoggedIn to false). I can remove this initialization. But then I get this error:

"Cannot declare entity named '$isLoggedIn'; the '$' prefix is reserved for implicitly-synthesized declarations" (for the static non isolated(safe)... expression)

For me, it's fine to disable this code until a fix is ready. So not particularly concerned about getting workarounds to resolve.

"Variable with getter/setter cannot have an initial value" (for setting isLoggedIn to false). I can remove this initialization. But then I get this error:

Ah apologies for the confusion there, you don't need to set the initial value for static var isLoggedIn: Bool. The initial value is actually picked up for the peer expansion's initializer: Tips.Parameter(Self.self, "+isLoggedIn", false).

struct ParameterRuleTip: Tip {
    // Define the app state you want to track.
static var isLoggedIn: Bool {
        get {
            $isLoggedIn.wrappedValue
        }
        set {
            $isLoggedIn.wrappedValue = newValue
        }
    }

    static nonisolated(unsafe) var _isLoggedIn: Tips.Parameter<Bool> = Tips.Parameter(Self.self, "+isLoggedIn", false)
}

"Cannot declare entity named '$isLoggedIn'; the '$' prefix is reserved for implicitly-synthesized declarations" (for the static non isolated(safe)... expression)

For the reserved $ prefix, you could change that and the rule to use an underscore or something else:

static nonisolated(unsafe) var _isLoggedIn: Tips.Parameter<Bool> = Tips.Parameter(Self.self, "+isLoggedIn", false)

var rules: [Rule] {
    #Rule(_isLoggedIn) { $0 == true }
}

But it should be resolved on the framework/macro side shortly as well!

TipKit vs. Swift 6 + Concurrency
 
 
Q