Skip to content

Instantly share code, notes, and snippets.

@inamiy
Last active May 20, 2023 02:02
Show Gist options
  • Save inamiy/18585ead2df825e2c00c5abb6967fca0 to your computer and use it in GitHub Desktop.
Save inamiy/18585ead2df825e2c00c5abb6967fca0 to your computer and use it in GitHub Desktop.
`@propertyWrapper` + `@MainActor var wrappedValue` which propagates `@MainActor` context to the encapsulating type. https://twitter.com/inamiy/status/1659530309501853696
@MainActor
class ViewModel: ObservableObject {}
@propertyWrapper
struct Wrapper<T> {
var wrappedValue: T
}
@propertyWrapper
struct MainWrapper<T> {
@MainActor var wrappedValue: T
}
//----------------------------------------
struct Foo1 {
var viewModel: ViewModel = ViewModel() // ERROR: Call to main actor-isolated initializer 'init()' in a synchronous nonisolated context
}
struct Foo2 {
@StateObject
var viewModel: ViewModel = ViewModel() // Compile OK
}
struct Foo3 {
@Wrapper
var viewModel: ViewModel = ViewModel() // ERROR: Call to main actor-isolated initializer 'init()' in a synchronous nonisolated context
}
struct Foo4 {
@MainWrapper
var viewModel: ViewModel = ViewModel() // Compile OK
}
struct Foo5 {
@MainWrapper
var viewModel: ViewModel = ViewModel() // Compile OK
// Compile OK, even though it looks same as Foo1,
// because `Foo5.init` will automatically gain `@MainActor` by `@MainWrapper`,
// and `MainWrapper.init` will automatically gain `@MainActor` by `@MainActor var wrappedValue`.
var viewModel2: ViewModel = ViewModel()
}
// NOTE: Requires `@MainActor`, although there is no `@MainActor Foo5` or `@MainActor init`.
func testFoo5() {
_ = Foo5() // ERROR: Call to main actor-isolated initializer 'init()' in a synchronous nonisolated context
}
@inamiy
Copy link
Author

inamiy commented May 20, 2023

swift-evolution/0316-global-actors.md at main · apple/swift-evolution · GitHub

A struct or class containing a wrapped instance property with a global actor-qualified wrappedValue infers actor isolation from that property wrapper:

@propertyWrapper
struct UIUpdating<Wrapped> {
  @MainActor var wrappedValue: Wrapped
}

struct CounterView { // infers @MainActor from use of @UIUpdating
  @UIUpdating var intValue: Int = 0
}

@inamiy
Copy link
Author

inamiy commented May 20, 2023

Another interesting fact when having multiple @globalActor-ed @propertyWrappers: https://twitter.com/kntkymt/status/1659595057929031680

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment