StoreKit sample project from Apple fails concurrency checks

I am trying to use code from Apple's sample project on StoreKit (Implementing a store in your app using the StoreKit API). However, even if I don't make any changes to this sample project except enable "complete" concurrency checking in the build settings, I get warnings like Capture of 'self' with non-sendable type 'Store' in a '@Sendable' closure. How worried should I be about these warnings and how can I fix them?

Accepted Reply

You should definitely file a bug against the sample. The plan of record is for these warnings to become errors in the future.

How worried should I be about these warnings … ?

If you’re writing new code you should strive to fix all strict concurrency warnings (because… you guessed it… these warnings will become errors).

how can I fix them?

That depends on the specifics of the error. In this case Store is a type defined in the sample like so:

class Store: ObservableObject {

	…

    init() {
		…
        Task {
            …
            await requestProducts()
            …
        }
    }

	…

    @MainActor
    func requestProducts() async {
		…
    }

	…
}

Swift is complaining because:

  • Store is not bound to any actor.

  • The task started by the initialiser is bound to the main actor.

That means that self is being passed across concurrency domains, and therefore must be sendable.

Notably, Store is an observable object, which means that it probably should be bound to the main actor. Do this like so:

@MainActor
class Store: ObservableObject {
    …
}

Once you do that:

  • The @MainActor on requestProducts() is redundant.

  • The task started by the initialiser is also bound to the main actor [1].

At that point Swift knows that the whole class is running on the main actor and thus self no longer needs to be sendable.

If you’re just coming up to speed on Swift concurrency I strongly recommend that you watch the WWDC ‘sailing on the sea of concurrency’ talk. You can find a link to it in Concurrency Resources.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] This is due to the magic of @_inheritActorContext, which something you can research independently (-:

  • That does the trick, thanks Quinn! I had been feeling cautious about putting everything on the @MainActor if it wasn't in the recommended approach as I've made subtle mistakes using StoreKit before. But looking at the specific functions involved it seems like it should be totally fine.

Add a Comment

Replies

You should definitely file a bug against the sample. The plan of record is for these warnings to become errors in the future.

How worried should I be about these warnings … ?

If you’re writing new code you should strive to fix all strict concurrency warnings (because… you guessed it… these warnings will become errors).

how can I fix them?

That depends on the specifics of the error. In this case Store is a type defined in the sample like so:

class Store: ObservableObject {

	…

    init() {
		…
        Task {
            …
            await requestProducts()
            …
        }
    }

	…

    @MainActor
    func requestProducts() async {
		…
    }

	…
}

Swift is complaining because:

  • Store is not bound to any actor.

  • The task started by the initialiser is bound to the main actor.

That means that self is being passed across concurrency domains, and therefore must be sendable.

Notably, Store is an observable object, which means that it probably should be bound to the main actor. Do this like so:

@MainActor
class Store: ObservableObject {
    …
}

Once you do that:

  • The @MainActor on requestProducts() is redundant.

  • The task started by the initialiser is also bound to the main actor [1].

At that point Swift knows that the whole class is running on the main actor and thus self no longer needs to be sendable.

If you’re just coming up to speed on Swift concurrency I strongly recommend that you watch the WWDC ‘sailing on the sea of concurrency’ talk. You can find a link to it in Concurrency Resources.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] This is due to the magic of @_inheritActorContext, which something you can research independently (-:

  • That does the trick, thanks Quinn! I had been feeling cautious about putting everything on the @MainActor if it wasn't in the recommended approach as I've made subtle mistakes using StoreKit before. But looking at the specific functions involved it seems like it should be totally fine.

Add a Comment